6.0 KiB
Task 005: Repair Parts Tracking
Status
Design specification. Not yet implemented.
Objective
Allow authorized operators to record parts installed, removed, or consumed during a repair while preserving immutable history and leaving room for a later spare-parts inventory system.
Product Intent
A technician should be able to determine what parts were installed, removed, or consumed; their quantity and identifying serial number where relevant; any concise notes; and the authoritative actor and timestamp.
Scope
- proposed focused repair-part read and action APIs
- proposed capability-based permissions
- a deliberately small part type and action vocabulary
- part type, action, quantity, optional serial number, and notes
- serialized component identifiers where applicable
- explicit void/correction rather than physical deletion
- immutable repair events
- migration, constraints, tests, and documentation
Out of Scope
Purchasing, suppliers, pricing, invoices, warehouse transfers, reorder automation, warranties, RMA, full inventory, generalized bill of materials, bulk import, automatic stock deduction, photos, work orders, and control-board identity replacement.
Task 004 remains authoritative for control-board identity and MAC changes.
Task 005 must not accept control_board as a generic part type or update
miners.control_board_sn, miners.current_mac, or
miner_network_identities.
API
Proposed routes:
GET /api/repairs/{repair_id}/parts
POST /api/repairs/{repair_id}/parts
POST /api/repairs/{repair_id}/parts/{repair_part_id}/void
Do not physically delete authoritative entries.
Permissions
The proposed authorization model is:
- use implemented
repairs.readfor the read route - introduce
repairs.parts.managefor add and void actions
Grant the proposed write permission to Admin, Manager, and Technician. Do not
grant it to Read Only. repairs.parts.manage does not exist yet and must be
introduced by the Task 005 migration. The router checks capabilities rather than
role names.
Add Request
{
"part_type": "fan",
"action": "installed",
"quantity": 1,
"serial_number": null,
"notes": "Replacement fan installed."
}
Initial Vocabulary
Proposed part types:
fanpower_supplyhashboardcableconsumableother
Proposed actions:
installedremovedconsumed
Task 005 does not model source inventory, stock movement, return destination, condition, or disposition. Those concepts require a later inventory decision and must not be approximated with overlapping action/value combinations here. Use notes for concise context that is not authoritative structured inventory data.
Quantity Rules
- quantity greater than zero
- integer quantities for this task
- serialized components normally quantity 1
- consumables may exceed 1
- voiding does not create a negative entry or reverse inventory; it marks the original record void
Repair State Rules
- add entries only while the repair is active
- void entries only while the repair is active
- read entries and void state after completion
- completed repairs remain read-only
- no unrestricted editing in any repair state
Data Model
Proposed table: repair_parts. No such table currently exists. The legacy
/api/repairs/parts-summary route summarizes generic maintenance-event data and
is not an implementation of this model.
Potential fields:
- UUID
- repair UUID
- part type
- action
- quantity
- serial number
- notes
- recorded actor and timestamp
- void actor, timestamp, and reason
- related event UUID
Entries are never physically deleted. A void changes only the one-way void metadata. It must not edit the original part type, action, quantity, serial number, notes, recording actor, or recording timestamp. The void reason is required; void actor and timestamp come from authentication and the server.
Constraints
- quantity > 0
- void fields all-null or all-present
- void timestamp after recorded timestamp
- actor and recorded timestamp required
- valid constrained values and combinations
- serialized entries with a serial number have quantity 1
Events
Proposed types:
REPAIR_PART_RECORDEDREPAIR_PART_VOIDED
The events are immutable history; the table is the queryable projection.
Atomicity
Adding a part and appending its event occur in one transaction. Voiding and appending the void event occur in one transaction.
Voiding must lock the repair-part row and succeed only when its void fields are
still null. Concurrent void requests must produce one success and one conflict,
with exactly one REPAIR_PART_VOIDED event.
Architecture
Preserve Router -> Service -> Repository -> PostgreSQL. No generic parts CRUD, router SQL, or physical deletion.
Tests
Cover:
- authorization
- serialized and consumable entries
- removed part
- list chronology
- read after completion
- explicit void
- invalid quantities and combinations
- control-board part rejection
- add to completed repair
- already-voided conflict
- cross-repair entry conflict
- concurrent void
- rollback on event failure
- direct constraints
- Tasks 001-004 regressions
- migration lifecycle and full backend suite
Migration
Create the focused table, add permissions and event constraints, add justified indexes, preserve legacy truth, and refuse destructive downgrade when authoritative Task 005 history exists.
Acceptance Criteria
- Parts can be recorded against an active repair.
- Historical entries remain readable.
- Entries are never physically deleted.
- Voids are explicit and audited.
- Actor and timestamps are authoritative.
- Writes and events are atomic.
- Values and quantities are constrained.
- No warehouse or purchasing system is introduced.
- Task 004 remains authoritative for control-board identity changes.
- No commit or push is performed.
Completion Report
Report files, migration, routes, permissions, schema, constraints, events, tests, migration lifecycle, architecture audit, limitations, and final status.
Do not commit. Do not push.