# 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: ```http 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.read` for the read route - introduce `repairs.parts.manage` for 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 ```json { "part_type": "fan", "action": "installed", "quantity": 1, "serial_number": null, "notes": "Replacement fan installed." } ``` ## Initial Vocabulary Proposed part types: - `fan` - `power_supply` - `hashboard` - `cable` - `consumable` - `other` Proposed actions: - `installed` - `removed` - `consumed` 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_RECORDED` - `REPAIR_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 1. Parts can be recorded against an active repair. 2. Historical entries remain readable. 3. Entries are never physically deleted. 4. Voids are explicit and audited. 5. Actor and timestamps are authoritative. 6. Writes and events are atomic. 7. Values and quantities are constrained. 8. No warehouse or purchasing system is introduced. 9. Task 004 remains authoritative for control-board identity changes. 10. 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.