from __future__ import annotations import uuid from datetime import date, datetime from decimal import Decimal from typing import Any from sqlalchemy import Date, DateTime, ForeignKey, Integer, Numeric, String, Text, func from sqlalchemy.orm import Mapped, mapped_column, relationship from sqlalchemy.types import JSON from app.db.base_class import Base class ExpenseClaim(Base): __tablename__ = "expense_claims" id: Mapped[str] = mapped_column(String(36), primary_key=True, default=lambda: str(uuid.uuid4())) claim_no: Mapped[str] = mapped_column(String(50), unique=True, index=True) employee_id: Mapped[str | None] = mapped_column( ForeignKey("employees.id"), nullable=True, index=True ) employee_name: Mapped[str] = mapped_column(String(100), index=True) department_id: Mapped[str | None] = mapped_column( ForeignKey("organization_units.id"), nullable=True, index=True ) department_name: Mapped[str] = mapped_column(String(100), index=True) project_code: Mapped[str | None] = mapped_column(String(50), nullable=True) expense_type: Mapped[str] = mapped_column(String(50), index=True) reason: Mapped[str] = mapped_column(Text()) location: Mapped[str] = mapped_column(String(100)) amount: Mapped[Decimal] = mapped_column(Numeric(12, 2)) currency: Mapped[str] = mapped_column(String(10), default="CNY") invoice_count: Mapped[int] = mapped_column(Integer, default=0) occurred_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), index=True) submitted_at: Mapped[datetime | None] = mapped_column( DateTime(timezone=True), nullable=True, index=True ) status: Mapped[str] = mapped_column(String(30), index=True) approval_stage: Mapped[str | None] = mapped_column(String(50), nullable=True) risk_flags_json: Mapped[list[Any]] = mapped_column(JSON, default=list) created_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), server_default=func.now()) updated_at: Mapped[datetime] = mapped_column( DateTime(timezone=True), server_default=func.now(), onupdate=func.now() ) items = relationship( "ExpenseClaimItem", back_populates="claim", cascade="all, delete-orphan", order_by="asc(ExpenseClaimItem.item_date)", ) class ExpenseClaimItem(Base): __tablename__ = "expense_claim_items" id: Mapped[str] = mapped_column(String(36), primary_key=True, default=lambda: str(uuid.uuid4())) claim_id: Mapped[str] = mapped_column(ForeignKey("expense_claims.id"), index=True) item_date: Mapped[date] = mapped_column(Date(), index=True) item_type: Mapped[str] = mapped_column(String(50)) item_reason: Mapped[str] = mapped_column(Text()) item_location: Mapped[str] = mapped_column(String(100)) item_amount: Mapped[Decimal] = mapped_column(Numeric(12, 2)) invoice_id: Mapped[str | None] = mapped_column(String(100), nullable=True) created_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), server_default=func.now()) updated_at: Mapped[datetime] = mapped_column( DateTime(timezone=True), server_default=func.now(), onupdate=func.now() ) claim = relationship("ExpenseClaim", back_populates="items") class AccountsReceivableRecord(Base): __tablename__ = "accounts_receivable" id: Mapped[str] = mapped_column(String(36), primary_key=True, default=lambda: str(uuid.uuid4())) receivable_no: Mapped[str] = mapped_column(String(50), unique=True, index=True) customer_id: Mapped[str] = mapped_column(String(64), index=True) customer_name: Mapped[str] = mapped_column(String(120), index=True) contract_no: Mapped[str | None] = mapped_column(String(100), nullable=True) invoice_no: Mapped[str | None] = mapped_column(String(100), nullable=True) amount_receivable: Mapped[Decimal] = mapped_column(Numeric(12, 2)) amount_received: Mapped[Decimal] = mapped_column(Numeric(12, 2)) amount_outstanding: Mapped[Decimal] = mapped_column(Numeric(12, 2)) currency: Mapped[str] = mapped_column(String(10), default="CNY") posting_date: Mapped[date] = mapped_column(Date(), index=True) due_date: Mapped[date] = mapped_column(Date(), index=True) aging_days: Mapped[int] = mapped_column(Integer, default=0) status: Mapped[str] = mapped_column(String(30), index=True) risk_flags_json: Mapped[list[Any]] = mapped_column(JSON, default=list) created_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), server_default=func.now()) updated_at: Mapped[datetime] = mapped_column( DateTime(timezone=True), server_default=func.now(), onupdate=func.now() ) class AccountsPayableRecord(Base): __tablename__ = "accounts_payable" id: Mapped[str] = mapped_column(String(36), primary_key=True, default=lambda: str(uuid.uuid4())) payable_no: Mapped[str] = mapped_column(String(50), unique=True, index=True) vendor_id: Mapped[str] = mapped_column(String(64), index=True) vendor_name: Mapped[str] = mapped_column(String(120), index=True) invoice_no: Mapped[str | None] = mapped_column(String(100), nullable=True) amount_payable: Mapped[Decimal] = mapped_column(Numeric(12, 2)) amount_paid: Mapped[Decimal] = mapped_column(Numeric(12, 2)) amount_outstanding: Mapped[Decimal] = mapped_column(Numeric(12, 2)) currency: Mapped[str] = mapped_column(String(10), default="CNY") posting_date: Mapped[date] = mapped_column(Date(), index=True) due_date: Mapped[date] = mapped_column(Date(), index=True) aging_days: Mapped[int] = mapped_column(Integer, default=0) status: Mapped[str] = mapped_column(String(30), index=True) risk_flags_json: Mapped[list[Any]] = mapped_column(JSON, default=list) created_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), server_default=func.now()) updated_at: Mapped[datetime] = mapped_column( DateTime(timezone=True), server_default=func.now(), onupdate=func.now() )