1f5234a3e9
- Replace single-battery add form with bulk add (brand + count, auto-generated labels) - Add device-level install form: specify brand+qty pairs, system autoselects available batteries - Add bulk actions on dashboard: retire, delete, unassign, change brand (checkbox multi-select) - Keep per-battery assign route for special cases (e.g. known low-capacity battery) - Remove unique constraint on Battery.label (labels are now auto-generated) - Add *.snapshot to .gitignore for DB snapshot files - Rewrite tests: 35 passing (11 new tests for bulk-add, device-install, bulk-actions) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
53 lines
1.6 KiB
Python
53 lines
1.6 KiB
Python
from sqlalchemy import Column, Integer, String, Text, ForeignKey
|
|
from sqlalchemy.orm import declarative_base, relationship
|
|
|
|
Base = declarative_base()
|
|
|
|
|
|
class Device(Base):
|
|
__tablename__ = "device"
|
|
|
|
id = Column(Integer, primary_key=True, autoincrement=True)
|
|
name = Column(String(100), nullable=False, unique=True)
|
|
battery_slots = Column(Integer, nullable=False, default=1)
|
|
notes = Column(Text, nullable=True)
|
|
|
|
batteries = relationship("Battery", back_populates="device")
|
|
|
|
def installed_count(self):
|
|
return sum(1 for b in self.batteries if b.status == "installed")
|
|
|
|
def installed_brands(self):
|
|
return set(b.brand for b in self.batteries if b.status == "installed")
|
|
|
|
def has_mixed_brands(self):
|
|
return len(self.installed_brands()) > 1
|
|
|
|
def __repr__(self):
|
|
return f"<Device {self.name}>"
|
|
|
|
|
|
class Battery(Base):
|
|
__tablename__ = "battery"
|
|
|
|
id = Column(Integer, primary_key=True, autoincrement=True)
|
|
label = Column(String(50), nullable=False)
|
|
brand = Column(String(100), nullable=False)
|
|
status = Column(String(20), nullable=False, default="available")
|
|
device_id = Column(Integer, ForeignKey("device.id", ondelete="SET NULL"), nullable=True)
|
|
notes = Column(Text, nullable=True)
|
|
|
|
device = relationship("Device", back_populates="batteries")
|
|
|
|
def is_available(self):
|
|
return self.status == "available"
|
|
|
|
def is_retired(self):
|
|
return self.status == "retired"
|
|
|
|
def is_installed(self):
|
|
return self.status == "installed"
|
|
|
|
def __repr__(self):
|
|
return f"<Battery {self.label}>"
|