feat: Add spool adjustment history tracking (#6)

Track history of all spool weight/length adjustments with timestamps and comments:

Backend:
- Add SpoolAdjustment database model with spool_id, timestamp, type, value, comment
- Add Alembic migration for spool_adjustment table
- Add SpoolAdjustment Pydantic model for API responses
- Update use_weight, use_length, and measure functions to record adjustments
- Add GET /api/v1/spool/{id}/adjustments endpoint for history retrieval
- Add optional comment parameter to use/measure endpoints

Frontend:
- Add ISpoolAdjustment interface to spool model
- Add collapsible adjustment history table to spool show page
- Display timestamp, type (weight/length), amount, and comment
- Add translation keys for history table

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
2026-01-14 20:18:56 -06:00
parent 388669d0c6
commit d804329b77
8 changed files with 307 additions and 13 deletions

View File

@@ -0,0 +1,43 @@
"""add_spool_adjustment_history.
Revision ID: b2c3d4e5f6g7
Revises: a1b2c3d4e5f6
Create Date: 2025-01-15 03:00:00.000000
"""
import sqlalchemy as sa
from alembic import op
# revision identifiers, used by Alembic.
revision = "b2c3d4e5f6g7"
down_revision = "a1b2c3d4e5f6"
branch_labels = None
depends_on = None
def upgrade() -> None:
"""Create spool_adjustment table for tracking history."""
op.create_table(
"spool_adjustment",
sa.Column("id", sa.Integer(), nullable=False),
sa.Column("spool_id", sa.Integer(), nullable=False),
sa.Column("timestamp", sa.DateTime(), nullable=False),
sa.Column("adjustment_type", sa.String(length=16), nullable=False),
sa.Column("value", sa.Float(), nullable=False),
sa.Column("comment", sa.String(length=256), nullable=True),
sa.ForeignKeyConstraint(
["spool_id"],
["spool.id"],
ondelete="CASCADE",
),
sa.PrimaryKeyConstraint("id"),
)
op.create_index(op.f("ix_spool_adjustment_id"), "spool_adjustment", ["id"], unique=False)
op.create_index(op.f("ix_spool_adjustment_spool_id"), "spool_adjustment", ["spool_id"], unique=False)
def downgrade() -> None:
"""Drop spool_adjustment table."""
op.drop_index(op.f("ix_spool_adjustment_spool_id"), table_name="spool_adjustment")
op.drop_index(op.f("ix_spool_adjustment_id"), table_name="spool_adjustment")
op.drop_table("spool_adjustment")