JhHardwareWRS_BackPoint/app/models.py
2026-07-25 19:57:18 +08:00

553 lines
27 KiB
Python

from datetime import date, datetime
from enum import StrEnum
from sqlalchemy import (
BigInteger,
Boolean,
Date,
DateTime,
Enum,
ForeignKey,
Index,
Integer,
Numeric,
String,
Text,
text,
)
from sqlalchemy.dialects.mysql import JSON
from sqlalchemy.orm import Mapped, mapped_column, relationship
from app.database import Base
from app.timezone import now
class Role(StrEnum):
worker = "worker"
admin = "admin"
manager = "manager"
class SessionStatus(StrEnum):
active = "active"
reporting = "reporting"
submitted = "submitted"
cancelled = "cancelled"
class ReportStatus(StrEnum):
pending = "pending"
approved = "approved"
rejected = "rejected"
class AuditAction(StrEnum):
approve = "approve"
approve_with_correction = "approve_with_correction"
reject = "reject"
manager_break_update = "manager_break_update"
class AttendancePoint(Base):
__tablename__ = "attendance_points"
name: Mapped[str] = mapped_column(String(128), primary_key=True)
latitude: Mapped[float | None] = mapped_column(Numeric(10, 7))
longitude: Mapped[float | None] = mapped_column(Numeric(10, 7))
radius_meters: Mapped[int] = mapped_column(Integer, nullable=False, default=500)
day_start: Mapped[str] = mapped_column(String(5), nullable=False, default="08:00")
day_end: Mapped[str] = mapped_column(String(5), nullable=False, default="17:20")
lunch_start: Mapped[str] = mapped_column(String(5), nullable=False, default="11:40")
lunch_end: Mapped[str] = mapped_column(String(5), nullable=False, default="12:40")
dinner_start: Mapped[str] = mapped_column(String(5), nullable=False, default="17:20")
dinner_end: Mapped[str] = mapped_column(String(5), nullable=False, default="18:00")
overtime_start: Mapped[str] = mapped_column(String(5), nullable=False, default="18:00")
overtime_end: Mapped[str] = mapped_column(String(5), nullable=False, default="20:00")
night_start: Mapped[str] = mapped_column(String(5), nullable=False, default="20:00")
night_end: Mapped[str] = mapped_column(String(5), nullable=False, default="06:00")
remark: Mapped[str | None] = mapped_column(String(500))
is_active: Mapped[bool] = mapped_column(Boolean, nullable=False, default=True)
created_at: Mapped[datetime] = mapped_column(DateTime, default=now, nullable=False)
updated_at: Mapped[datetime] = mapped_column(DateTime, default=now, onupdate=now, nullable=False)
class Personnel(Base):
__tablename__ = "personnel"
phone: Mapped[str] = mapped_column(String(20), primary_key=True)
name: Mapped[str] = mapped_column(String(64), nullable=False)
is_temporary: Mapped[bool] = mapped_column(Boolean, nullable=False, default=False)
temporary_expires_at: Mapped[datetime | None] = mapped_column(DateTime)
created_at: Mapped[datetime] = mapped_column(DateTime, default=now, nullable=False)
updated_at: Mapped[datetime] = mapped_column(DateTime, default=now, onupdate=now, nullable=False)
roles: Mapped[list["PersonRole"]] = relationship(
back_populates="person",
cascade="all, delete-orphan",
)
attendance_points: Mapped[list["PersonAttendancePoint"]] = relationship(
back_populates="person",
cascade="all, delete-orphan",
)
class PersonRole(Base):
__tablename__ = "person_roles"
phone: Mapped[str] = mapped_column(ForeignKey("personnel.phone"), primary_key=True)
role: Mapped[Role] = mapped_column(Enum(Role), primary_key=True)
created_at: Mapped[datetime] = mapped_column(DateTime, default=now, nullable=False)
person: Mapped[Personnel] = relationship(back_populates="roles")
class PersonAttendancePoint(Base):
__tablename__ = "person_attendance_points"
phone: Mapped[str] = mapped_column(ForeignKey("personnel.phone"), primary_key=True)
attendance_point_name: Mapped[str] = mapped_column(
ForeignKey("attendance_points.name"),
primary_key=True,
)
created_at: Mapped[datetime] = mapped_column(DateTime, default=now, nullable=False)
person: Mapped[Personnel] = relationship(back_populates="attendance_points")
attendance_point: Mapped[AttendancePoint] = relationship()
class Product(Base):
__tablename__ = "products"
attendance_point_name: Mapped[str] = mapped_column(String(128), primary_key=True, default="")
project_no: Mapped[str] = mapped_column(String(64), primary_key=True)
product_name: Mapped[str] = mapped_column(String(255), primary_key=True)
device_no: Mapped[str] = mapped_column(String(64), primary_key=True)
process_name: Mapped[str] = mapped_column(String(128), primary_key=True, default="")
profile_no: Mapped[str | None] = mapped_column(String(128))
material_code: Mapped[str | None] = mapped_column(String(128))
material_name: Mapped[str | None] = mapped_column(String(255))
supplier: Mapped[str | None] = mapped_column(String(255))
product_net_weight_kg: Mapped[float | None] = mapped_column(Numeric(12, 4))
product_gross_weight_kg: Mapped[float | None] = mapped_column(Numeric(12, 4))
scrap_loss_rate: Mapped[float | None] = mapped_column(Numeric(10, 6))
waste_price_yuan_per_kg: Mapped[float | None] = mapped_column(Numeric(12, 4))
stamping_method: Mapped[str | None] = mapped_column(String(128))
operator_count: Mapped[float] = mapped_column(Numeric(10, 2), nullable=False, default=1)
process_unit_price_yuan: Mapped[float] = mapped_column(Numeric(12, 4), nullable=False, default=0)
standard_beat: Mapped[float] = mapped_column(Numeric(12, 3), nullable=False)
standard_workload: 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)
__table_args__ = (
Index("idx_products_device_no", "device_no"),
Index("idx_products_material_code", "material_code"),
)
class Equipment(Base):
__tablename__ = "equipment"
attendance_point_name: Mapped[str] = mapped_column(String(128), primary_key=True, default="")
device_no: Mapped[str] = mapped_column(String(64), primary_key=True)
device_type: Mapped[str] = mapped_column(String(32), nullable=False, default="冲压设备")
remark: Mapped[str | None] = mapped_column(String(500))
created_at: Mapped[datetime] = mapped_column(DateTime, default=now, nullable=False)
updated_at: Mapped[datetime] = mapped_column(DateTime, default=now, onupdate=now, nullable=False)
class WorkSchedule(Base):
__tablename__ = "work_schedules"
id: Mapped[int] = mapped_column(Integer, primary_key=True, default=1)
day_start: Mapped[str] = mapped_column(String(5), nullable=False, default="08:00")
day_end: Mapped[str] = mapped_column(String(5), nullable=False, default="17:20")
lunch_start: Mapped[str] = mapped_column(String(5), nullable=False, default="11:40")
lunch_end: Mapped[str] = mapped_column(String(5), nullable=False, default="12:40")
dinner_start: Mapped[str] = mapped_column(String(5), nullable=False, default="17:20")
dinner_end: Mapped[str] = mapped_column(String(5), nullable=False, default="18:00")
overtime_start: Mapped[str] = mapped_column(String(5), nullable=False, default="18:00")
overtime_end: Mapped[str] = mapped_column(String(5), nullable=False, default="20:00")
night_start: Mapped[str] = mapped_column(String(5), nullable=False, default="20:00")
night_end: Mapped[str] = mapped_column(String(5), nullable=False, default="06:00")
attendance_latitude: Mapped[float | None] = mapped_column(Numeric(10, 7))
attendance_longitude: Mapped[float | None] = mapped_column(Numeric(10, 7))
attendance_radius_meters: Mapped[int] = mapped_column(Integer, nullable=False, default=500)
auto_submit_hours: Mapped[float] = mapped_column(Numeric(10, 2), nullable=False, default=15)
updated_by: Mapped[str | None] = mapped_column(String(20))
created_at: Mapped[datetime] = mapped_column(DateTime, default=now, nullable=False)
updated_at: Mapped[datetime] = mapped_column(DateTime, default=now, onupdate=now, nullable=False)
class WorkSession(Base):
__tablename__ = "work_sessions"
id: Mapped[int] = mapped_column(BigInteger, primary_key=True, autoincrement=True)
attendance_point_name: Mapped[str] = mapped_column(String(128), nullable=False, default="")
employee_phone: Mapped[str] = mapped_column(ForeignKey("personnel.phone"), nullable=False)
start_at: Mapped[datetime] = mapped_column(DateTime, nullable=False)
end_at: Mapped[datetime | None] = mapped_column(DateTime)
status: Mapped[SessionStatus] = mapped_column(
Enum(SessionStatus),
nullable=False,
default=SessionStatus.active,
)
created_at: Mapped[datetime] = mapped_column(DateTime, default=now, nullable=False)
updated_at: Mapped[datetime] = mapped_column(DateTime, default=now, onupdate=now, nullable=False)
employee: Mapped[Personnel] = relationship()
devices: Mapped[list["WorkSessionDevice"]] = relationship(
back_populates="session",
cascade="all, delete-orphan",
order_by="WorkSessionDevice.sort_order",
)
__table_args__ = (
Index("idx_sessions_employee_status", "employee_phone", "status"),
)
class WorkSessionDevice(Base):
__tablename__ = "work_session_devices"
id: Mapped[int] = mapped_column(BigInteger, primary_key=True, autoincrement=True)
session_id: Mapped[int] = mapped_column(ForeignKey("work_sessions.id"), nullable=False)
attendance_point_name: Mapped[str] = mapped_column(String(128), nullable=False, default="")
device_no: Mapped[str] = mapped_column(String(255), nullable=False)
process_name: Mapped[str] = mapped_column(String(128), nullable=False, default="")
scanned_at: Mapped[datetime] = mapped_column(DateTime, nullable=False)
sort_order: Mapped[int] = mapped_column(nullable=False, default=0)
released_at: Mapped[datetime | None] = mapped_column(DateTime)
released_by: Mapped[str | None] = mapped_column(String(20))
release_reason: Mapped[str | None] = mapped_column(String(120))
session: Mapped[WorkSession] = relationship(back_populates="devices")
__table_args__ = (
Index("idx_session_devices_session", "session_id"),
Index("idx_session_devices_device", "device_no"),
Index("idx_session_devices_release", "released_at"),
)
class ProductionReport(Base):
__tablename__ = "production_reports"
id: Mapped[int] = mapped_column(BigInteger, primary_key=True, autoincrement=True)
attendance_point_name: Mapped[str] = mapped_column(String(128), nullable=False, default="")
session_id: Mapped[int] = mapped_column(ForeignKey("work_sessions.id"), nullable=False, unique=True)
employee_phone: Mapped[str] = mapped_column(ForeignKey("personnel.phone"), nullable=False)
report_date: Mapped[date] = mapped_column(Date, nullable=False)
start_at: Mapped[datetime] = mapped_column(DateTime, nullable=False)
end_at: Mapped[datetime] = mapped_column(DateTime, nullable=False)
duration_minutes: Mapped[float] = mapped_column(Numeric(10, 2), nullable=False, default=0)
break_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)
total_good_qty: Mapped[float] = mapped_column(Numeric(12, 2), nullable=False, default=0)
total_output_qty: Mapped[float] = mapped_column(Numeric(12, 2), nullable=False, default=0)
actual_beat: Mapped[float] = mapped_column(Numeric(12, 3), nullable=False, default=0)
standard_beat: Mapped[float] = mapped_column(Numeric(12, 3), nullable=False, default=0)
expected_workload: Mapped[float] = mapped_column(Numeric(12, 2), nullable=False, default=0)
pace_rate: Mapped[float] = mapped_column(Numeric(10, 2), nullable=False, default=0)
workload_rate: Mapped[float] = mapped_column(Numeric(10, 2), nullable=False, default=0)
has_over_limit: Mapped[bool] = mapped_column(
Boolean,
nullable=False,
default=False,
server_default=text("0"),
)
over_limit_summary: Mapped[str | None] = mapped_column(String(500))
status: Mapped[ReportStatus] = mapped_column(
Enum(ReportStatus),
nullable=False,
default=ReportStatus.pending,
)
reviewer_phone: Mapped[str | None] = mapped_column(ForeignKey("personnel.phone"))
reviewed_at: Mapped[datetime | None] = mapped_column(DateTime)
reject_reason: Mapped[str | None] = mapped_column(String(500))
review_remark: Mapped[str | None] = mapped_column(String(500))
submitted_at: Mapped[datetime] = mapped_column(DateTime, default=now, nullable=False)
is_system_auto_submitted: Mapped[bool] = mapped_column(Boolean, nullable=False, default=False)
auto_submit_reason: Mapped[str | None] = mapped_column(String(120))
is_multi_person_assistant: Mapped[bool] = mapped_column(Boolean, nullable=False, default=False)
multi_person_source_report_id: Mapped[int | None] = mapped_column(BigInteger)
is_voided: Mapped[bool] = mapped_column(Boolean, nullable=False, default=False)
voided_at: Mapped[datetime | None] = mapped_column(DateTime)
voided_by: Mapped[str | None] = mapped_column(ForeignKey("personnel.phone"))
unvoid_deadline_at: Mapped[datetime | None] = mapped_column(DateTime)
updated_at: Mapped[datetime] = mapped_column(DateTime, default=now, onupdate=now, nullable=False)
employee: Mapped[Personnel] = relationship(foreign_keys=[employee_phone])
reviewer: Mapped[Personnel | None] = relationship(foreign_keys=[reviewer_phone])
voider: Mapped[Personnel | None] = relationship(foreign_keys=[voided_by])
session: Mapped[WorkSession] = relationship()
items: Mapped[list["ProductionReportItem"]] = relationship(
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",
)
__table_args__ = (
Index("idx_reports_employee_date", "employee_phone", "report_date"),
Index("idx_reports_status_date", "status", "report_date"),
Index("idx_reports_reviewer", "reviewer_phone", "reviewed_at"),
Index("idx_reports_voided", "is_voided", "voided_at"),
Index("idx_reports_over_limit", "has_over_limit", "submitted_at"),
)
class ProductionReportItem(Base):
__tablename__ = "production_report_items"
id: Mapped[int] = mapped_column(BigInteger, primary_key=True, autoincrement=True)
report_id: Mapped[int] = mapped_column(ForeignKey("production_reports.id"), nullable=False)
attendance_point_name: Mapped[str] = mapped_column(String(128), nullable=False, default="")
device_no: Mapped[str] = mapped_column(String(255), nullable=False)
project_no: Mapped[str] = mapped_column(String(64), nullable=False)
product_name: Mapped[str] = mapped_column(String(255), nullable=False)
material_code: Mapped[str | None] = mapped_column(String(128))
material_name: Mapped[str | None] = mapped_column(String(255))
raw_material_batch_no: Mapped[str | None] = mapped_column(String(128))
process_name: Mapped[str | None] = mapped_column(String(128))
stamping_method: Mapped[str | None] = mapped_column(String(128))
operator_count: Mapped[float] = mapped_column(Numeric(10, 2), nullable=False, default=1)
process_unit_price_yuan: Mapped[float] = mapped_column(Numeric(12, 4), nullable=False, default=0)
changeover_count: Mapped[float] = mapped_column(Numeric(12, 2), nullable=False, default=0)
standard_beat: Mapped[float] = mapped_column(Numeric(12, 3), nullable=False, default=0)
standard_workload: Mapped[float] = mapped_column(Numeric(12, 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)
over_limit_status: Mapped[str] = mapped_column(
String(32),
nullable=False,
default="not_checked",
server_default="not_checked",
)
over_limit_type: Mapped[str | None] = mapped_column(String(64))
over_limit_reason: Mapped[str | None] = mapped_column(String(500))
over_limit_checked_at: Mapped[datetime | None] = mapped_column(DateTime)
over_limit_check_source: Mapped[str | None] = mapped_column(String(32))
over_limit_batch_no: Mapped[str | None] = mapped_column(String(128))
over_limit_product_gross_weight_kg: Mapped[float | None] = mapped_column(Numeric(12, 4))
over_limit_issued_weight_kg: Mapped[float | None] = mapped_column(Numeric(18, 6))
over_limit_max_reportable_qty: Mapped[float | None] = mapped_column(Numeric(12, 2))
over_limit_previous_good_qty: Mapped[float | None] = mapped_column(Numeric(12, 2))
over_limit_current_cumulative_qty: Mapped[float | None] = mapped_column(Numeric(12, 2))
over_limit_current_report_qty: Mapped[float | None] = mapped_column(Numeric(12, 2))
allocated_minutes: Mapped[float] = mapped_column(Numeric(10, 2), nullable=False, default=0)
started_at: Mapped[datetime | None] = mapped_column(DateTime)
remark: Mapped[str | None] = mapped_column(String(500))
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"),
Index("idx_report_items_product", "project_no", "product_name"),
Index("idx_report_items_over_limit_status", "over_limit_status"),
Index(
"idx_report_items_process_batch",
"attendance_point_name",
"project_no",
"product_name",
"raw_material_batch_no",
"process_name",
),
)
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(
"uq_report_allocations_report_item_date",
"report_id",
"report_item_id",
"allocation_date",
unique=True,
),
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"
id: Mapped[int] = mapped_column(BigInteger, primary_key=True, autoincrement=True)
report_id: Mapped[int] = mapped_column(ForeignKey("production_reports.id"), nullable=False)
reviewer_phone: Mapped[str] = mapped_column(ForeignKey("personnel.phone"), nullable=False)
action: Mapped[AuditAction] = mapped_column(Enum(AuditAction), nullable=False)
before_json: Mapped[dict | None] = mapped_column(JSON)
after_json: Mapped[dict | None] = mapped_column(JSON)
remark: Mapped[str | None] = mapped_column(String(500))
created_at: Mapped[datetime] = mapped_column(DateTime, default=now, nullable=False)
__table_args__ = (
Index("idx_audit_report", "report_id"),
Index("idx_audit_reviewer", "reviewer_phone", "created_at"),
)
class MoldLockFeedback(Base):
__tablename__ = "mold_lock_feedbacks"
id: Mapped[int] = mapped_column(BigInteger, primary_key=True, autoincrement=True)
attendance_point_name: Mapped[str] = mapped_column(String(128), nullable=False, default="")
mold_name: Mapped[str] = mapped_column(String(255), nullable=False)
process_name: Mapped[str] = mapped_column(String(128), nullable=False, default="")
session_device_id: Mapped[int] = mapped_column(ForeignKey("work_session_devices.id"), nullable=False)
reporter_phone: Mapped[str] = mapped_column(ForeignKey("personnel.phone"), nullable=False)
occupied_phone: Mapped[str] = mapped_column(ForeignKey("personnel.phone"), nullable=False)
status: Mapped[str] = mapped_column(String(32), nullable=False, default="pending")
read_at: Mapped[datetime | None] = mapped_column(DateTime)
handled_at: Mapped[datetime | None] = mapped_column(DateTime)
handled_by: Mapped[str | None] = mapped_column(ForeignKey("personnel.phone"))
created_at: Mapped[datetime] = mapped_column(DateTime, default=now, nullable=False)
session_device: Mapped[WorkSessionDevice] = relationship()
reporter: Mapped[Personnel] = relationship(foreign_keys=[reporter_phone])
occupied_person: Mapped[Personnel] = relationship(foreign_keys=[occupied_phone])
handler: Mapped[Personnel | None] = relationship(foreign_keys=[handled_by])
__table_args__ = (
Index("idx_mold_lock_feedback_point_read", "attendance_point_name", "read_at", "created_at"),
Index("idx_mold_lock_feedback_status", "status", "created_at"),
)
class DeviceQRCode(Base):
__tablename__ = "device_qrcodes"
attendance_point_name: Mapped[str] = mapped_column(String(128), primary_key=True, default="")
device_no: Mapped[str] = mapped_column(String(255), primary_key=True)
process_name: Mapped[str] = mapped_column(String(128), primary_key=True, default="")
qr_scene: Mapped[str] = mapped_column(String(128), nullable=False)
qr_url: Mapped[str | None] = mapped_column(String(500))
created_by: Mapped[str] = mapped_column(ForeignKey("personnel.phone"), nullable=False)
created_at: Mapped[datetime] = mapped_column(DateTime, default=now, nullable=False)
class DeviceQrBatchTask(Base):
__tablename__ = "device_qrcode_batch_tasks"
id: Mapped[int] = mapped_column(BigInteger, primary_key=True, autoincrement=True)
file_name: Mapped[str] = mapped_column(String(255), nullable=False)
status: Mapped[str] = mapped_column(String(32), nullable=False, default="pending")
item_count: Mapped[int] = mapped_column(Integer, nullable=False, default=0)
completed_count: Mapped[int] = mapped_column(Integer, nullable=False, default=0)
failed_count: Mapped[int] = mapped_column(Integer, nullable=False, default=0)
items_json: Mapped[list] = mapped_column(JSON, nullable=False)
zip_url: Mapped[str | None] = mapped_column(String(500))
error_message: Mapped[str | None] = mapped_column(String(1000))
created_by: Mapped[str] = mapped_column(ForeignKey("personnel.phone"), nullable=False)
started_at: Mapped[datetime | None] = mapped_column(DateTime)
finished_at: Mapped[datetime | None] = mapped_column(DateTime)
created_at: Mapped[datetime] = mapped_column(DateTime, default=now, nullable=False)
updated_at: Mapped[datetime] = mapped_column(DateTime, default=now, onupdate=now, nullable=False)
creator: Mapped[Personnel] = relationship()
__table_args__ = (
Index("idx_device_qrcode_batch_tasks_creator", "created_by", "created_at"),
Index("idx_device_qrcode_batch_tasks_status", "status", "created_at"),
)
class Notice(Base):
__tablename__ = "notices"
id: Mapped[int] = mapped_column(BigInteger, primary_key=True, autoincrement=True)
title: Mapped[str] = mapped_column(String(120), nullable=False)
content: Mapped[str] = mapped_column(Text, nullable=False)
sort_order: Mapped[int] = mapped_column(Integer, nullable=False, default=0)
is_active: Mapped[bool] = mapped_column(Boolean, nullable=False, default=True)
created_by: Mapped[str] = mapped_column(ForeignKey("personnel.phone"), nullable=False)
created_at: Mapped[datetime] = mapped_column(DateTime, default=now, nullable=False)
updated_at: Mapped[datetime] = mapped_column(DateTime, default=now, onupdate=now, nullable=False)
creator: Mapped[Personnel] = relationship()
attendance_points: Mapped[list["NoticePoint"]] = relationship(
back_populates="notice",
cascade="all, delete-orphan",
)
__table_args__ = (
Index("idx_notices_active_sort", "is_active", "sort_order"),
Index("idx_notices_updated", "updated_at"),
)
class NoticePoint(Base):
__tablename__ = "notice_points"
notice_id: Mapped[int] = mapped_column(ForeignKey("notices.id"), primary_key=True)
attendance_point_name: Mapped[str] = mapped_column(
ForeignKey("attendance_points.name"),
primary_key=True,
)
created_at: Mapped[datetime] = mapped_column(DateTime, default=now, nullable=False)
notice: Mapped[Notice] = relationship(back_populates="attendance_points")
attendance_point: Mapped[AttendancePoint] = relationship()
class ReconciliationLedgerEntry(Base):
__tablename__ = "reconciliation_ledger_entries"
attendance_point_name: Mapped[str] = mapped_column(String(128), primary_key=True, default="")
year: Mapped[int] = mapped_column(Integer, primary_key=True)
month: Mapped[int] = mapped_column(Integer, primary_key=True)
product_name: Mapped[str] = mapped_column(String(255), primary_key=True)
reconciled_good_qty: Mapped[float] = mapped_column(Numeric(14, 2), nullable=False, default=0)
return_qty: Mapped[float] = mapped_column(Numeric(14, 2), nullable=False, default=0)
updated_by: Mapped[str | None] = mapped_column(String(20))
created_at: Mapped[datetime] = mapped_column(DateTime, default=now, nullable=False)
updated_at: Mapped[datetime] = mapped_column(DateTime, default=now, onupdate=now, nullable=False)
__table_args__ = (
Index("idx_reconciliation_product", "product_name"),
Index("idx_reconciliation_updated", "updated_at"),
)