From 8692bdf8e2b2240a5dbd6ac9b5da35eb838fe699 Mon Sep 17 00:00:00 2001 From: souplearn Date: Sat, 25 Jul 2026 02:30:02 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E5=A2=9E=E5=8A=A0=E6=8A=A5=E5=B7=A5?= =?UTF-8?q?=E5=BD=92=E5=B1=9E=E6=98=8E=E7=BB=86=E8=A1=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/models.py | 46 ++++++++++++++ scripts/migrate_report_allocations.py | 89 +++++++++++++++++++++++++++ 2 files changed, 135 insertions(+) create mode 100644 scripts/migrate_report_allocations.py diff --git a/app/models.py b/app/models.py index bb77538..f008bb3 100644 --- a/app/models.py +++ b/app/models.py @@ -277,6 +277,10 @@ class ProductionReport(Base): back_populates="report", cascade="all, delete-orphan", ) + allocations: Mapped[list["ProductionReportAllocation"]] = relationship( + back_populates="report", + cascade="all, delete-orphan", + ) audit_logs: Mapped[list["ReportAuditLog"]] = relationship( order_by="ReportAuditLog.created_at", ) @@ -317,6 +321,10 @@ class ProductionReportItem(Base): created_at: Mapped[datetime] = mapped_column(DateTime, default=now, nullable=False) report: Mapped[ProductionReport] = relationship(back_populates="items") + allocations: Mapped[list["ProductionReportAllocation"]] = relationship( + back_populates="item", + cascade="all, delete-orphan", + ) __table_args__ = ( Index("idx_report_items_report", "report_id"), @@ -324,6 +332,44 @@ class ProductionReportItem(Base): ) +class ProductionReportAllocation(Base): + __tablename__ = "production_report_allocations" + + id: Mapped[int] = mapped_column(BigInteger, primary_key=True, autoincrement=True) + report_id: Mapped[int] = mapped_column( + ForeignKey("production_reports.id", ondelete="CASCADE"), + nullable=False, + ) + report_item_id: Mapped[int | None] = mapped_column( + ForeignKey("production_report_items.id", ondelete="CASCADE"), + ) + attendance_point_name: Mapped[str] = mapped_column(String(128), nullable=False, default="") + employee_phone: Mapped[str] = mapped_column(String(20), nullable=False, default="") + allocation_date: Mapped[date] = mapped_column(Date, nullable=False) + day_minutes: Mapped[float] = mapped_column(Numeric(10, 2), nullable=False, default=0) + overtime_minutes: Mapped[float] = mapped_column(Numeric(10, 2), nullable=False, default=0) + night_minutes: Mapped[float] = mapped_column(Numeric(10, 2), nullable=False, default=0) + effective_minutes: Mapped[float] = mapped_column(Numeric(10, 2), nullable=False, default=0) + good_qty: Mapped[float] = mapped_column(Numeric(12, 2), nullable=False, default=0) + defect_qty: Mapped[float] = mapped_column(Numeric(12, 2), nullable=False, default=0) + scrap_qty: Mapped[float] = mapped_column(Numeric(12, 2), nullable=False, default=0) + changeover_count: Mapped[float] = mapped_column(Numeric(12, 2), nullable=False, default=0) + reference_wage: Mapped[float] = mapped_column(Numeric(12, 2), nullable=False, default=0) + created_at: Mapped[datetime] = mapped_column(DateTime, default=now, nullable=False) + updated_at: Mapped[datetime] = mapped_column(DateTime, default=now, onupdate=now, nullable=False) + + report: Mapped[ProductionReport] = relationship(back_populates="allocations") + item: Mapped[ProductionReportItem | None] = relationship(back_populates="allocations") + + __table_args__ = ( + Index("idx_report_allocations_report", "report_id"), + Index("idx_report_allocations_item", "report_item_id"), + Index("idx_report_allocations_date", "allocation_date"), + Index("idx_report_allocations_point_date", "attendance_point_name", "allocation_date"), + Index("idx_report_allocations_employee_date", "employee_phone", "allocation_date"), + ) + + class ReportAuditLog(Base): __tablename__ = "report_audit_logs" diff --git a/scripts/migrate_report_allocations.py b/scripts/migrate_report_allocations.py new file mode 100644 index 0000000..d7e8514 --- /dev/null +++ b/scripts/migrate_report_allocations.py @@ -0,0 +1,89 @@ +from pathlib import Path +import sys + +from sqlalchemy import text + +ROOT = Path(__file__).resolve().parents[1] +sys.path.insert(0, str(ROOT)) + +from app.database import engine # noqa: E402 + + +def index_exists(conn, table_name: str, index_name: str) -> bool: + return bool( + conn.execute( + text( + """ + SELECT COUNT(1) + FROM information_schema.statistics + WHERE table_schema = DATABASE() + AND table_name = :table_name + AND index_name = :index_name + """ + ), + {"table_name": table_name, "index_name": index_name}, + ).scalar() + ) + + +def create_index(conn, table_name: str, index_name: str, columns: str) -> None: + if not index_exists(conn, table_name, index_name): + conn.execute(text(f"CREATE INDEX {index_name} ON {table_name} ({columns})")) + + +def main() -> None: + with engine.begin() as conn: + conn.execute( + text( + """ + CREATE TABLE IF NOT EXISTS production_report_allocations ( + id BIGINT NOT NULL AUTO_INCREMENT, + report_id BIGINT NOT NULL, + report_item_id BIGINT NULL, + attendance_point_name VARCHAR(128) NOT NULL DEFAULT '', + employee_phone VARCHAR(20) NOT NULL DEFAULT '', + allocation_date DATE NOT NULL, + day_minutes DECIMAL(10, 2) NOT NULL DEFAULT 0, + overtime_minutes DECIMAL(10, 2) NOT NULL DEFAULT 0, + night_minutes DECIMAL(10, 2) NOT NULL DEFAULT 0, + effective_minutes DECIMAL(10, 2) NOT NULL DEFAULT 0, + good_qty DECIMAL(12, 2) NOT NULL DEFAULT 0, + defect_qty DECIMAL(12, 2) NOT NULL DEFAULT 0, + scrap_qty DECIMAL(12, 2) NOT NULL DEFAULT 0, + changeover_count DECIMAL(12, 2) NOT NULL DEFAULT 0, + reference_wage DECIMAL(12, 2) NOT NULL DEFAULT 0, + created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, + updated_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, + PRIMARY KEY (id), + CONSTRAINT fk_report_allocations_report + FOREIGN KEY (report_id) REFERENCES production_reports (id) + ON DELETE CASCADE, + CONSTRAINT fk_report_allocations_item + FOREIGN KEY (report_item_id) REFERENCES production_report_items (id) + ON DELETE CASCADE + ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci + """ + ) + ) + + create_index(conn, "production_report_allocations", "idx_report_allocations_report", "report_id") + create_index(conn, "production_report_allocations", "idx_report_allocations_item", "report_item_id") + create_index(conn, "production_report_allocations", "idx_report_allocations_date", "allocation_date") + create_index( + conn, + "production_report_allocations", + "idx_report_allocations_point_date", + "attendance_point_name, allocation_date", + ) + create_index( + conn, + "production_report_allocations", + "idx_report_allocations_employee_date", + "employee_phone, allocation_date", + ) + + print("report allocations schema migrated") + + +if __name__ == "__main__": + main()