from __future__ import annotations

from datetime import date, datetime
from decimal import Decimal

from pydantic import BaseModel, ConfigDict, Field


class ContactPayload(BaseModel):
    contact_name: str | None = None
    contact_phone: str | None = None


class SupplierRead(BaseModel):
    model_config = ConfigDict(from_attributes=True)

    id: int
    supplier_code: str
    supplier_name: str
    short_name: str | None = None
    contact_name: str | None = None
    contact_phone: str | None = None
    contacts: list[ContactPayload] = Field(default_factory=list, max_length=3)
    address: str | None = None
    lead_time_days: int
    default_tax_rate: float
    status: str
    remark: str | None = None


class SupplierCreate(BaseModel):
    supplier_name: str
    short_name: str | None = None
    contact_name: str | None = None
    contact_phone: str | None = None
    contacts: list[ContactPayload] = Field(default_factory=list, max_length=3)
    address: str | None = None
    lead_time_days: int = 0
    default_tax_rate: float = 0
    status: str = "ACTIVE"
    remark: str | None = None


class SupplierUpdate(SupplierCreate):
    pass


class WorkCenterRead(BaseModel):
    id: int
    center_code: str
    center_name: str
    dept_id: int
    dept_name: str | None = None
    center_type: str
    shift_pattern: str | None = None
    capacity_hours_per_day: float
    status: str


class WarehouseLocationRead(BaseModel):
    id: int
    warehouse_id: int
    warehouse_code: str
    warehouse_name: str
    location_code: str
    location_name: str
    zone_name: str | None = None
    is_default: bool
    is_locked: bool
    status: str


class PurchaseOrderItemCreate(BaseModel):
    material_item_id: int
    source_demand_id: int | None = None
    order_qty: float = 0
    order_weight_kg: float = 0
    unit_price: float = 0
    remark: str | None = None


class PurchaseOrderCreate(BaseModel):
    supplier_id: int
    order_date: date | None = None
    expected_date: date | None = None
    warning_lead_days: int = Field(default=0, ge=0)
    purchaser_employee_id: int | None = None
    target_warehouse_type: str = "RAW"
    sales_order_ids: list[int] = Field(default_factory=list)
    tax_rate: float = 0
    remark: str | None = None
    items: list[PurchaseOrderItemCreate]


class PurchaseOrderUpdate(PurchaseOrderCreate):
    pass


class PurchaseOrderRead(BaseModel):
    purchase_order_id: int
    po_no: str
    source_type: str = "STOCKING"
    source_order_nos: str | None = None
    sales_order_ids: list[int] = Field(default_factory=list)
    supplier_id: int
    supplier_code: str
    supplier_name: str
    order_date: date
    expected_date: date | None = None
    warning_lead_days: int = 0
    warning_start_date: date | None = None
    is_warning_active: bool = False
    purchaser_employee_id: int | None = None
    purchaser_name: str | None = None
    target_warehouse_type: str = "RAW"
    tax_rate: float
    total_amount: float
    status: str
    display_status: str | None = None
    remark: str | None = None
    line_count: int
    total_order_qty: float = 0
    total_order_weight_kg: float
    total_received_qty: float = 0
    total_received_weight_kg: float
    has_return: bool = False
    total_return_weight_kg: float = 0
    archive_status: str = "未生成"
    archive_version: int | None = None
    archive_created_at: datetime | None = None
    archive_error_message: str | None = None


class PurchaseOrderItemRead(BaseModel):
    purchase_order_item_id: int
    purchase_order_id: int
    po_no: str
    line_no: int
    material_item_id: int
    material_code: str
    material_name: str
    specification: str | None = None
    material_grade: str | None = None
    performance_requirement: str | None = None
    purchase_calc_mode: str | None = None
    source_demand_id: int | None = None
    order_qty: float
    order_weight_kg: float
    received_qty: float
    received_weight_kg: float
    returned_weight_kg: float = 0
    net_received_weight_kg: float = 0
    has_return: bool = False
    unit_price: float
    line_amount: float
    status: str
    display_status: str | None = None
    remark: str | None = None


class PurchaseReceiptItemCreate(BaseModel):
    purchase_order_item_id: int
    material_item_id: int
    location_id: int | None = None
    lot_no: str | None = None
    received_qty: float = 0
    received_weight_kg: float = 0
    unit_cost: float = 0
    remark: str | None = None


class PurchaseReceiptCreate(BaseModel):
    purchase_order_id: int
    warehouse_id: int
    receipt_date: datetime | None = None
    receiver_employee_id: int | None = None
    supplier_delivery_no: str | None = None
    waybill_no: str | None = None
    order_photo_url: str | None = None
    freight_amount: float | None = None
    remark: str | None = None
    items: list[PurchaseReceiptItemCreate]


class PurchaseReceiptRead(BaseModel):
    receipt_id: int
    receipt_no: str
    purchase_order_id: int
    po_no: str
    supplier_id: int
    supplier_name: str
    warehouse_id: int
    warehouse_name: str
    target_warehouse_type: str = "RAW"
    receipt_date: datetime
    receiver_employee_id: int | None = None
    receiver_name: str | None = None
    supplier_delivery_no: str | None = None
    logistics_waybill_no: str | None = None
    logistics_freight_amount: float | None = None
    logistics_photo_url: str | None = None
    material_names: str | None = None
    status: str
    remark: str | None = None
    line_count: int
    total_received_qty: float = 0
    total_received_weight_kg: float
    archive_status: str = "未生成"
    archive_version: int | None = None
    archive_created_at: datetime | None = None
    archive_error_message: str | None = None


class PurchaseReceiptItemRead(BaseModel):
    receipt_item_id: int
    receipt_id: int
    receipt_no: str
    line_no: int
    purchase_order_item_id: int
    material_item_id: int
    material_code: str
    material_name: str
    warehouse_id: int
    warehouse_name: str
    location_id: int | None = None
    location_name: str | None = None
    lot_no: str
    received_qty: float
    received_weight_kg: float
    accepted_qty: float
    accepted_weight_kg: float
    unit_cost: float
    status: str
    quality_status: str | None = None
    remark: str | None = None


class StockLotPurchaseLinkRead(BaseModel):
    purchase_order_id: int
    po_no: str
    purchase_order_item_id: int
    material_item_id: int
    material_code: str
    material_name: str
    lot_id: int
    lot_no: str
    accepted_weight_kg: float
    returned_weight_kg: float
    returnable_weight_kg: float
    unit_cost: float


class RawMaterialReturnOutboundItemCreate(BaseModel):
    lot_id: int
    purchase_order_item_id: int
    return_weight_kg: float
    reason: str | None = None
    remark: str | None = None


class RawMaterialReturnOutboundCreate(BaseModel):
    warehouse_id: int
    return_date: datetime | None = None
    waybill_no: str | None = None
    order_photo_url: str | None = None
    freight_amount: float | None = None
    reason: str | None = None
    remark: str | None = None
    items: list[RawMaterialReturnOutboundItemCreate]


class PurchaseReturnItemRead(BaseModel):
    purchase_return_item_id: int
    purchase_return_id: int
    line_no: int
    purchase_order_item_id: int
    material_item_id: int
    material_code: str
    material_name: str
    lot_id: int
    lot_no: str
    return_weight_kg: float
    unit_cost: float
    amount: float
    reason: str | None = None
    status: str
    remark: str | None = None


class PurchaseReturnRead(BaseModel):
    purchase_return_id: int
    return_no: str
    purchase_order_id: int
    po_no: str
    warehouse_id: int
    warehouse_name: str
    return_date: datetime
    logistics_waybill_no: str | None = None
    logistics_freight_amount: float | None = None
    logistics_photo_url: str | None = None
    reason: str | None = None
    status: str
    remark: str | None = None
    total_return_weight_kg: float
    total_amount: float
    items: list[PurchaseReturnItemRead] = Field(default_factory=list)
    archive_status: str | None = None
    archive_business_id: int | None = None
    archive_document_type: str | None = None
    archive_error_message: str | None = None


class StockLotRead(BaseModel):
    lot_id: int
    lot_no: str
    parent_lot_id: int | None = None
    lot_role: str | None = None
    material_sub_batch_no: str | None = None
    item_id: int
    item_code: str
    item_name: str
    item_type: str
    specification: str | None = None
    item_remark: str | None = None
    stock_unit_name: str | None = None
    purchase_unit_name: str | None = None
    sales_unit_name: str | None = None
    warehouse_id: int
    warehouse_name: str
    warehouse_type: str | None = None
    location_id: int | None = None
    location_name: str | None = None
    source_doc_type: str
    source_doc_id: int
    source_line_id: int | None = None
    source_receipt_no: str | None = None
    source_purchase_order_no: str | None = None
    source_material_lot_id: int | None = None
    source_material_lot_no: str | None = None
    source_material_sub_batch_no: str | None = None
    source_material_summary: str | None = None
    logistics_waybill_no: str | None = None
    logistics_freight_amount: float | None = None
    logistics_photo_url: str | None = None
    inbound_qty: float
    inbound_weight_kg: float
    remaining_qty: float
    remaining_weight_kg: float
    locked_qty: float
    locked_weight_kg: float
    unit_cost: float
    quality_status: str
    quality_user_name: str | None = None
    status: str
    created_at: datetime
    archive_status: str | None = None
    archive_business_id: int | None = None
    archive_document_type: str | None = None
    archive_error_message: str | None = None


class StockLotOptionRead(BaseModel):
    lot_id: int
    lot_no: str
    item_id: int
    item_code: str
    item_name: str
    warehouse_id: int
    warehouse_name: str
    remaining_weight_kg: float
    unit_cost: float
    quality_status: str
    status: str
    is_available_for_issue: bool
    disabled_reason: str | None = None


class StockBalanceRead(BaseModel):
    stock_balance_id: int
    item_id: int
    item_code: str
    item_name: str
    item_type: str
    specification: str | None = None
    item_remark: str | None = None
    stock_unit_name: str | None = None
    purchase_unit_name: str | None = None
    sales_unit_name: str | None = None
    warehouse_id: int
    warehouse_name: str
    warehouse_type: str | None = None
    location_id: int | None = None
    location_name: str | None = None
    qty_on_hand: float
    weight_on_hand_kg: float
    qty_available: float
    weight_available_kg: float
    qty_allocated: float
    weight_allocated_kg: float
    avg_unit_cost: float
    updated_at: datetime


class InventoryTxnRead(BaseModel):
    inventory_txn_id: int
    txn_no: str
    txn_type: str
    item_id: int
    item_code: str
    item_name: str
    warehouse_id: int
    warehouse_name: str
    warehouse_type: str | None = None
    location_id: int | None = None
    location_name: str | None = None
    lot_id: int | None = None
    lot_no: str | None = None
    qty_change: float
    weight_change_kg: float
    unit_cost: float
    amount: float
    source_doc_type: str
    source_doc_id: int
    source_line_id: int | None = None
    source_material_lot_id: int | None = None
    source_material_lot_no: str | None = None
    source_material_sub_batch_no: str | None = None
    source_material_summary: str | None = None
    biz_time: datetime
    logistics_waybill_no: str | None = None
    logistics_freight_amount: float | None = None
    logistics_photo_url: str | None = None
    remark: str | None = None
    archive_status: str = "未生成"
    archive_business_id: int | None = None
    archive_document_type: str | None = None
    archive_error_message: str | None = None


class InventoryTxnLedgerRowRead(InventoryTxnRead):
    direction: str
    source_line_id: int | None = None
    operator_user_id: int | None = None
    operator_name: str | None = None


class InventoryTxnLedgerSummaryRead(BaseModel):
    in_count: int = 0
    out_count: int = 0
    adjust_count: int = 0
    in_qty: float = 0
    out_qty: float = 0
    in_weight_kg: float = 0
    out_weight_kg: float = 0
    latest_biz_time: datetime | None = None


class InventoryTxnLedgerResponse(BaseModel):
    items: list[InventoryTxnLedgerRowRead]
    total: int
    summary: InventoryTxnLedgerSummaryRead


class SelectedStockLotIssueCreate(BaseModel):
    source_lot_id: int
    issued_weight_kg: float
    material_item_id: int | None = None


class WorkOrderCreate(BaseModel):
    source_sales_order_item_id: int | None = None
    product_item_id: int
    planned_qty: float = 0
    issue_weight_kg: float = 0
    selected_stock_lots: list[SelectedStockLotIssueCreate] = Field(default_factory=list)
    planned_start_time: datetime | None = None
    planned_end_time: datetime | None = None
    priority_level: int = 3
    auto_issue_materials: bool = False
    remark: str | None = None


class WorkOrderRead(BaseModel):
    work_order_id: int
    work_order_no: str
    work_order_type: str
    source_sales_order_item_id: int | None = None
    source_order_no: str | None = None
    product_item_id: int
    product_code: str
    product_name: str
    product_version_no: str | None = None
    route_id: int
    route_name: str
    bom_id: int
    planned_qty: float
    released_qty: float
    finished_qty: float
    scrap_qty: float
    planned_start_time: datetime | None = None
    planned_end_time: datetime | None = None
    actual_start_time: datetime | None = None
    actual_end_time: datetime | None = None
    priority_level: int
    status: str
    remark: str | None = None
    operation_count: int
    abnormal_operation_count: int
    reported_good_qty: float = 0
    received_qty: float = 0
    max_receivable_qty: float = 0
    all_operations_done: bool = False
    archive_status: str | None = None
    archive_business_id: int | None = None
    archive_document_type: str | None = None
    archive_error_message: str | None = None


class WorkOrderMaterialRead(BaseModel):
    work_order_material_id: int
    work_order_id: int
    work_order_no: str
    bom_item_id: int
    material_item_id: int
    material_code: str
    material_name: str
    required_qty: float
    required_weight_kg: float
    issued_qty: float
    issued_weight_kg: float
    returned_qty: float
    returned_weight_kg: float
    material_sub_batch_summary: str | None = None
    issue_amount: float = 0
    status: str
    remark: str | None = None


class WorkOrderMaterialIssueRead(BaseModel):
    material_issue_id: int
    work_order_material_id: int
    work_order_id: int
    work_order_no: str
    material_item_id: int
    material_code: str
    material_name: str
    source_lot_id: int
    source_lot_no: str
    material_sub_batch_no: str
    issued_qty: float
    issued_weight_kg: float
    unit_cost: float
    amount: float
    issue_time: datetime
    remark: str | None = None


class ProductionBatchLedgerTxnRead(BaseModel):
    production_ledger_txn_id: int
    production_ledger_id: int
    txn_type: str
    qty_delta: float = 0
    weight_delta_kg: float = 0
    outside_weight_after_kg: float = 0
    source_doc_type: str | None = None
    source_doc_id: int | None = None
    source_line_id: int | None = None
    biz_time: datetime
    operator_user_id: int | None = None
    remark: str | None = None
    archive_status: str | None = None
    archive_business_id: int | None = None
    archive_document_type: str | None = None
    archive_error_message: str | None = None


class ProductionBatchLedgerRead(BaseModel):
    production_ledger_id: int
    material_lot_id: int
    material_lot_no: str
    material_item_id: int
    material_code: str | None = None
    material_name: str | None = None
    product_item_id: int
    product_code: str | None = None
    product_name: str | None = None
    status: str
    miniapp_selectable: bool = True
    total_issued_weight_kg: float = 0
    outside_weight_kg: float = 0
    finished_inbound_qty: float = 0
    surplus_inbound_weight_kg: float = 0
    scrap_inbound_weight_kg: float = 0
    writeoff_weight_kg: float = 0
    first_issue_time: datetime | None = None
    last_issue_time: datetime | None = None
    locked_at: datetime | None = None
    reopened_at: datetime | None = None
    lock_count: int = 0
    remark: str | None = None
    material_out_archive_status: str | None = None
    material_out_archive_business_id: int | None = None
    material_out_archive_document_type: str | None = None
    material_out_archive_error_message: str | None = None
    txns: list[ProductionBatchLedgerTxnRead] = Field(default_factory=list)


class ProductionBatchLedgerInboundPreviewRead(BaseModel):
    production_ledger_id: int
    material_lot_no: str
    product_item_id: int
    product_code: str | None = None
    product_name: str | None = None
    material_item_id: int
    material_code: str | None = None
    material_name: str | None = None
    gross_weight_per_piece_kg: float = 0
    net_weight_per_piece_kg: float = 0
    total_issued_weight_kg: float = 0
    outside_weight_kg: float = 0
    finished_inbound_qty: float = 0
    surplus_inbound_weight_kg: float = 0
    scrap_inbound_weight_kg: float = 0
    writeoff_weight_kg: float = 0
    reference_finished_qty: float = 0
    used_material_weight_kg: float = 0
    theoretical_surplus_weight_kg: float = 0
    theoretical_scrap_weight_kg: float = 0


class ProductionBatchLedgerInboundCreate(BaseModel):
    production_ledger_id: int
    finished_qty: float = 0
    surplus_weight_kg: float = 0
    scrap_weight_kg: float = 0
    lock_material_batch: bool = False
    lock_confirmed: bool = False
    deviation_remark: str | None = None
    remark: str | None = None


class ProductionBatchLedgerInboundRead(BaseModel):
    production_ledger_id: int
    material_lot_no: str
    finished_qty: float = 0
    surplus_weight_kg: float = 0
    scrap_weight_kg: float = 0
    finished_lot_id: int | None = None
    surplus_lot_id: int | None = None
    scrap_lot_id: int | None = None
    finished_txn_id: int | None = None
    surplus_txn_id: int | None = None
    scrap_txn_id: int | None = None
    locked: bool = False
    status: str
    remark: str | None = None
    archive_status: str | None = None
    archive_business_id: int | None = None
    archive_document_type: str | None = None
    archive_error_message: str | None = None


class WorkOrderOperationRead(BaseModel):
    work_order_operation_id: int
    work_order_id: int
    work_order_no: str
    route_operation_id: int
    seq_no: int
    operation_name: str
    work_center_id: int
    work_center_name: str
    planned_qty: float
    report_qty: float
    good_qty: float
    scrap_qty: float
    rework_qty: float
    std_labor_cost: float
    std_machine_cost: float
    std_outsource_cost: float
    allowed_scrap_rate: float
    is_abnormal: bool
    status: str
    remark: str | None = None


class WorkOrderLedgerMaterialRead(BaseModel):
    material_item_id: int
    material_code: str
    material_name: str
    source_lot_id: int
    source_lot_no: str
    issued_weight_kg: float
    returned_weight_kg: float = 0
    unit_cost: float = 0
    amount: float = 0


class WorkOrderLedgerOperationRead(BaseModel):
    work_order_operation_id: int
    seq_no: int
    operation_name: str
    report_qty: float
    good_qty: float
    scrap_qty: float
    status: str


class WorkOrderLedgerLimitRead(BaseModel):
    gross_weight_per_piece_kg: float
    net_weight_per_piece_kg: float
    issued_weight_kg: float
    used_material_weight_kg: float
    returned_material_weight_kg: float
    final_operation_good_qty: float
    finished_received_qty: float
    total_operation_scrap_qty: float
    scrap_inbound_weight_kg: float
    max_finished_inbound_qty: float
    max_scrap_inbound_weight_kg: float
    max_surplus_inbound_weight_kg: float
    tolerance_finished_inbound_qty: float
    tolerance_scrap_inbound_weight_kg: float
    tolerance_surplus_inbound_weight_kg: float


class WorkOrderLedgerRead(BaseModel):
    work_order_id: int
    work_order_no: str
    product_item_id: int
    product_code: str
    product_name: str
    planned_qty: float
    status: str
    remark: str | None = None
    material_summary: str
    source_lot_summary: str
    issued_weight_kg: float
    used_material_weight_kg: float
    returned_material_weight_kg: float
    finished_received_qty: float
    max_finished_inbound_qty: float
    max_scrap_inbound_weight_kg: float
    max_surplus_inbound_weight_kg: float
    limits: WorkOrderLedgerLimitRead
    materials: list[WorkOrderLedgerMaterialRead]
    operations: list[WorkOrderLedgerOperationRead]


class ProductionWorkOrderInboundPreviewRead(BaseModel):
    work_order_id: int
    work_order_no: str
    product_item_id: int
    product_code: str
    product_name: str
    source_lot_id: int
    source_lot_no: str
    material_item_id: int
    material_code: str
    material_name: str
    gross_weight_per_piece_kg: float
    net_weight_per_piece_kg: float
    issued_weight_kg: float
    returned_weight_kg: float
    final_operation_good_qty: float
    finished_inbound_qty: float
    reported_scrap_qty: float
    scrap_inbound_weight_kg: float
    reference_finished_qty: float
    remaining_reference_finished_qty: float
    finished_qty_upper_limit: float
    finished_qty: float
    theoretical_surplus_weight_kg: float
    theoretical_scrap_weight_kg: float


class ProductionWorkOrderInboundCreate(BaseModel):
    work_order_id: int
    finished_qty: float = 0
    surplus_weight_kg: float = 0
    scrap_weight_kg: float = 0
    settle_work_order: bool = False
    settle_confirmed: bool = False
    deviation_remark: str | None = None
    remark: str | None = None


class ProductionWorkOrderInboundRead(BaseModel):
    work_order_id: int
    work_order_no: str
    finished_qty: float = 0
    surplus_weight_kg: float = 0
    scrap_weight_kg: float = 0
    finished_lot_id: int | None = None
    surplus_lot_id: int | None = None
    scrap_lot_id: int | None = None
    finished_txn_id: int | None = None
    surplus_txn_id: int | None = None
    scrap_txn_id: int | None = None
    settled: bool = False
    status: str
    remark: str | None = None
    archive_status: str | None = None
    archive_business_id: int | None = None
    archive_document_type: str | None = None
    archive_error_message: str | None = None


class OperationReportCreate(BaseModel):
    work_order_id: int
    work_order_operation_id: int
    employee_id: int
    equipment_id: int | None = None
    report_source: str = "MANUAL"
    shift_code: str | None = None
    start_time: datetime
    end_time: datetime
    report_qty: float
    good_qty: float
    scrap_qty: float = 0
    rework_qty: float = 0
    scrap_reason: str | None = None
    downtime_minutes: float = 0
    extra_cost_amount: float = 0
    remark: str | None = None


class OperationReportRead(BaseModel):
    report_id: int
    report_no: str
    work_order_id: int
    work_order_no: str
    work_order_operation_id: int
    seq_no: int
    operation_name: str
    employee_id: int
    employee_name: str
    equipment_id: int | None = None
    equipment_name: str | None = None
    report_source: str
    shift_code: str | None = None
    start_time: datetime
    end_time: datetime
    report_qty: float
    good_qty: float
    scrap_qty: float
    rework_qty: float
    downtime_minutes: float
    extra_cost_amount: float
    is_abnormal: bool
    ref_cycle_seconds: float = 0
    actual_cycle_seconds: float | None = None
    abnormal_reason: str | None = None
    scrap_reason: str | None = None
    remark: str | None = None


class MiniAppOperationReportRead(BaseModel):
    source_report_id: int
    source_item_id: int
    report_no: str
    attendance_point_name: str = ""
    employee_phone: str
    employee_name: str
    report_date: date
    start_time: datetime
    end_time: datetime
    duration_minutes: float = 0
    effective_minutes: float = 0
    status: str
    status_name: str
    reviewer_phone: str | None = None
    reviewed_at: datetime | None = None
    reject_reason: str | None = None
    device_no: str
    project_no: str
    product_name: str
    material_code: str | None = None
    material_name: str | None = None
    raw_material_batch_no: str | None = None
    process_name: str | None = None
    standard_beat: float = 0
    standard_workload: float = 0
    good_qty: float = 0
    defect_qty: float = 0
    scrap_qty: float = 0
    erp_scrap_qty: float = 0
    allocated_minutes: float = 0
    started_at: datetime | None = None
    work_order_id: int | None = None
    work_order_no: str | None = None
    work_order_operation_id: int | None = None
    operation_name: str | None = None
    erp_report_id: int | None = None
    erp_report_no: str | None = None
    is_synced_to_erp: bool = False
    abnormal_reason: str | None = None


class MiniAppOperationReportSyncRead(BaseModel):
    sync_from: datetime | None = None
    sync_to: datetime
    last_synced_at: datetime
    total: int = 0
    synced: int = 0
    skipped: int = 0
    failed: int = 0


class MiniAppOperationReportSyncStateRead(BaseModel):
    last_synced_at: datetime | None = None


class MiniAppScrapRecordCreate(BaseModel):
    source_item_id: int
    scrap_qty: float = 0
    scrap_weight_kg: float = 0
    scrap_reason: str | None = None
    saleable_scrap_weight_kg: float = 0
    scrap_cost_amount: float = 0


class ScrapRecordCreate(BaseModel):
    report_id: int
    work_order_id: int
    work_order_operation_id: int
    item_id: int
    scrap_qty: float
    scrap_weight_kg: float = 0
    scrap_reason: str | None = None
    disposal_type: str = "SCRAP"
    saleable_scrap_weight_kg: float = 0
    scrap_cost_amount: float = 0


class ScrapRecordRead(BaseModel):
    scrap_record_id: int
    scrap_no: str
    report_id: int
    report_no: str
    work_order_id: int
    work_order_no: str
    work_order_operation_id: int
    operation_name: str
    item_id: int
    item_code: str
    item_name: str
    scrap_qty: float
    scrap_weight_kg: float
    scrap_reason: str | None = None
    disposal_type: str
    saleable_scrap_weight_kg: float
    scrap_cost_amount: float
    status: str
    created_at: datetime


class CompletionReceiptItemCreate(BaseModel):
    product_item_id: int
    location_id: int | None = None
    receipt_qty: float
    receipt_weight_kg: float = 0
    remark: str | None = None


class CompletionReceiptCreate(BaseModel):
    work_order_id: int
    warehouse_id: int
    receipt_time: datetime | None = None
    receiver_employee_id: int | None = None
    remark: str | None = None
    settle_work_order: bool = False
    settle_remark: str | None = None
    items: list[CompletionReceiptItemCreate]


class CompletionReceiptRead(BaseModel):
    completion_receipt_id: int
    receipt_no: str
    work_order_id: int
    work_order_no: str
    product_item_id: int
    product_code: str
    product_name: str
    warehouse_id: int
    warehouse_name: str
    receipt_time: datetime
    receiver_employee_id: int | None = None
    receiver_name: str | None = None
    status: str
    line_count: int
    total_receipt_qty: float
    total_receipt_weight_kg: float
    remark: str | None = None


class CompletionReceiptItemRead(BaseModel):
    completion_receipt_item_id: int
    completion_receipt_id: int
    receipt_no: str
    line_no: int
    product_item_id: int
    product_code: str
    product_name: str
    warehouse_id: int
    warehouse_name: str
    location_id: int | None = None
    location_name: str | None = None
    lot_no: str
    source_material_lot_id: int | None = None
    source_material_lot_no: str | None = None
    source_material_sub_batch_no: str | None = None
    source_material_summary: str | None = None
    receipt_qty: float
    receipt_weight_kg: float
    unit_cost: float
    status: str
    remark: str | None = None


class DeliveryItemCreate(BaseModel):
    sales_order_item_id: int | None = None
    product_item_id: int
    lot_id: int | None = None
    delivery_qty: float
    delivery_weight_kg: float = 0
    unit_price: float = 0
    remark: str | None = None


class DeliveryCreate(BaseModel):
    sales_order_id: int | None = None
    customer_id: int | None = None
    warehouse_id: int
    delivery_date: datetime | None = None
    shipper_employee_id: int | None = None
    consignee_name: str | None = None
    consignee_phone: str | None = None
    delivery_address: str | None = None
    waybill_no: str | None = None
    order_photo_url: str | None = None
    freight_amount: float | None = None
    remark: str | None = None
    items: list[DeliveryItemCreate]


class DeliveryRead(BaseModel):
    delivery_id: int
    delivery_no: str
    sales_order_id: int | None = None
    customer_id: int | None = None
    order_no: str | None = None
    customer_name: str | None = None
    warehouse_id: int
    warehouse_name: str
    delivery_date: datetime
    shipper_employee_id: int | None = None
    shipper_name: str | None = None
    consignee_name: str | None = None
    consignee_phone: str | None = None
    delivery_address: str | None = None
    logistics_waybill_no: str | None = None
    logistics_freight_amount: float | None = None
    logistics_photo_url: str | None = None
    product_names: str | None = None
    status: str
    line_count: int
    total_delivery_qty: float
    total_line_amount: float
    remark: str | None = None
    archive_status: str | None = None
    archive_business_id: int | None = None
    archive_document_type: str | None = None
    archive_error_message: str | None = None


class DeliveryItemRead(BaseModel):
    delivery_item_id: int
    delivery_id: int
    delivery_no: str
    line_no: int
    sales_order_item_id: int | None = None
    order_no: str | None = None
    product_item_id: int
    product_code: str
    product_name: str
    lot_id: int | None = None
    lot_no: str | None = None
    source_material_lot_id: int | None = None
    source_material_lot_no: str | None = None
    source_material_code: str | None = None
    source_material_name: str | None = None
    source_material_sub_batch_no: str | None = None
    source_material_summary: str | None = None
    delivery_qty: float
    delivery_weight_kg: float
    unit_price: float
    line_amount: float
    status: str
    remark: str | None = None


class CustomerSuppliedInboundCreate(BaseModel):
    material_item_id: int
    warehouse_id: int
    location_id: int | None = None
    provider_name: str | None = None
    inbound_weight_kg: float
    lot_no: str | None = None
    remark: str | None = None


class InventorySettlementRead(BaseModel):
    settlement_type: str
    source_doc_type: str
    source_doc_id: int
    doc_no: str
    counterparty_name: str
    biz_date: datetime
    amount: float
    settled_amount: float = 0
    open_amount: float
    status: str
    remark: str | None = None


class EquipmentCreate(BaseModel):
    equipment_name: str
    category_name: str | None = None
    work_center_id: int
    manufacturer: str | None = None
    model_no: str | None = None
    install_date: date | None = None
    rated_power_kw: float = 0
    occupied_area_sqm: float = 0
    maintenance_cycle_hours: float = 0
    next_maintenance_at: datetime | None = None
    status: str = "IDLE"
    remark: str | None = None


class EquipmentRead(BaseModel):
    equipment_id: int
    equipment_code: str
    equipment_name: str
    category_name: str | None = None
    work_center_id: int
    work_center_name: str
    manufacturer: str | None = None
    model_no: str | None = None
    install_date: date | None = None
    rated_power_kw: float
    occupied_area_sqm: float
    maintenance_cycle_hours: float
    last_maintenance_at: datetime | None = None
    next_maintenance_at: datetime | None = None
    status: str
    remark: str | None = None


class MaintenanceOrderCreate(BaseModel):
    equipment_id: int
    maintenance_type: str
    start_time: datetime | None = None
    end_time: datetime | None = None
    downtime_minutes: float = 0
    owner_employee_id: int | None = None
    result_summary: str | None = None
    cost_amount: float = 0
    status: str = "CREATED"


class MaintenanceOrderRead(BaseModel):
    maintenance_order_id: int
    order_no: str
    equipment_id: int
    equipment_code: str
    equipment_name: str
    maintenance_type: str
    start_time: datetime | None = None
    end_time: datetime | None = None
    downtime_minutes: float
    owner_employee_id: int | None = None
    owner_name: str | None = None
    result_summary: str | None = None
    cost_amount: float
    status: str


class QualityInspectionRead(BaseModel):
    receipt_item_id: int
    receipt_id: int
    receipt_no: str
    po_no: str
    supplier_name: str
    target_warehouse_type: str = "RAW"
    material_item_id: int
    material_code: str
    material_name: str
    warehouse_name: str
    location_name: str | None = None
    lot_no: str
    received_qty: float
    received_weight_kg: float
    accepted_qty: float
    accepted_weight_kg: float
    status: str
    quality_status: str
    inspector_name: str | None = None
    receipt_date: datetime
    remark: str | None = None
    archive_status: str = "未生成"
    archive_version: int | None = None
    archive_created_at: datetime | None = None
    archive_error_message: str | None = None


class QualityInspectionAction(BaseModel):
    result: str
    remark: str | None = None


class AccountingPeriodCreate(BaseModel):
    year_no: int
    month_no: int
    start_date: date
    end_date: date
    close_status: str = "OPEN"


class AccountingPeriodRead(BaseModel):
    period_id: int
    period_code: str
    year_no: int
    month_no: int
    start_date: date
    end_date: date
    close_status: str
    closed_at: datetime | None = None


class OverheadEntryCreate(BaseModel):
    period_id: int
    overhead_type: str
    dept_id: int | None = None
    amount: float
    allocation_basis: str = "WORK_HOURS"
    description: str | None = None


class OverheadEntryRead(BaseModel):
    overhead_entry_id: int
    period_id: int
    period_code: str
    year_no: int
    month_no: int
    overhead_type: str
    dept_id: int | None = None
    dept_name: str | None = None
    amount: float
    allocation_basis: str
    description: str | None = None
    created_at: datetime


class ProductMaterialCostExpenseRead(BaseModel):
    key: str
    label: str
    amount: float


class ProductMaterialCostRowRead(BaseModel):
    work_order_id: int
    work_order_no: str
    row_no: int
    biz_date: date | None = None
    product_code: str
    customer_name: str | None = None
    product_name: str
    supply_mode: str
    delivery_note_no: str | None = None
    order_qty: float
    actual_production_qty: float
    shipped_qty: float
    inventory_qty: float
    inventory_value: float
    untaxed_unit_price: float
    untaxed_total_amount: float
    purchase_material_weight_kg: float
    used_material_weight_kg: float
    material_inventory_weight_kg: float
    material_inventory_producible_qty: float
    material_inventory_value: float
    material_grade: str | None = None
    material_unit_price_kg: float
    gross_weight_g: float
    gross_material_cost_per_piece: float
    gross_material_total_cost: float
    net_weight_g: float
    scrap_weight_g: float
    scrap_unit_price_kg: float
    scrap_revenue_per_piece: float
    scrap_total_revenue: float
    material_cost_per_piece: float
    material_total_cost: float
    loss_rate: float
    loss_total_cost: float
    labor_cost_per_piece: float
    labor_total_cost: float
    outsource_cost_per_piece: float
    outsource_total_cost: float
    product_cost_per_piece: float
    product_cost_with_adjustment_per_piece: float
    product_total_cost: float
    rework_qty: float
    rework_cost_per_piece: float
    rework_total_cost: float
    scrap_qty: float
    scrap_residual_value_per_piece: float
    scrap_total_cost: float
    batch_adjustment_avg_cost: float


class ProductMaterialCostSummaryRead(BaseModel):
    row_count: int
    sales_total: float
    gross_material_total: float
    scrap_revenue_total: float
    material_cost_total: float
    material_loss_total: float
    labor_total: float
    outsource_total: float
    rework_total: float
    scrap_total: float
    overhead_total: float
    direct_cost_total: float
    profit_amount: float
    cost_ratio: float


class ProductMaterialCostBoardRead(BaseModel):
    title: str
    date_from: date
    date_to: date
    generated_at: datetime
    rows: list[ProductMaterialCostRowRead]
    expense_rows: list[ProductMaterialCostExpenseRead]
    summary: ProductMaterialCostSummaryRead


class CostAllocationRead(BaseModel):
    cost_allocation_id: int
    period_id: int
    period_code: str
    work_order_id: int
    work_order_no: str
    product_item_id: int
    product_code: str
    product_name: str
    allocation_basis_value: float
    overhead_amount: float
    material_amount: float
    labor_amount: float
    machine_amount: float
    abnormal_loss_amount: float
    total_cost_amount: float
    unit_cost: float


class CostAllocationGenerateResult(BaseModel):
    period_id: int
    period_code: str
    generated_count: int
    rows: list[CostAllocationRead]


class StatementSnapshotCreate(BaseModel):
    period_id: int
    statement_type: str
    report_date: date


class StatementSnapshotRead(BaseModel):
    statement_snapshot_id: int
    snapshot_no: str
    period_id: int
    period_code: str
    statement_type: str
    report_date: date
    summary_json: dict
    created_at: datetime


class ReturnItemCreate(BaseModel):
    sales_order_item_id: int | None = None
    delivery_item_id: int | None = None
    product_item_id: int
    source_lot_id: int | None = None
    return_qty: float
    return_weight_kg: float = 0
    responsibility_type: str | None = None
    remark: str | None = None


class ReturnOrderCreate(BaseModel):
    customer_id: int
    sales_order_id: int | None = None
    delivery_id: int | None = None
    return_date: datetime | None = None
    return_reason: str | None = None
    handler_employee_id: int | None = None
    warehouse_id: int | None = None
    remark: str | None = None
    items: list[ReturnItemCreate]


class ReturnScrapInboundItemCreate(BaseModel):
    delivery_item_id: int
    product_item_id: int
    source_lot_id: int | None = None
    scrap_qty: float = 0
    scrap_weight_kg: float
    unit_cost: float | None = None
    remark: str | None = None


class ReturnScrapInboundCreate(BaseModel):
    customer_id: int
    delivery_id: int
    warehouse_id: int
    inbound_date: datetime | None = None
    return_reason: str | None = None
    handler_employee_id: int | None = None
    remark: str | None = None
    items: list[ReturnScrapInboundItemCreate]


class ReturnOrderRead(BaseModel):
    return_order_id: int
    return_no: str
    customer_id: int
    customer_name: str
    sales_order_id: int | None = None
    order_no: str | None = None
    delivery_id: int | None = None
    delivery_no: str | None = None
    return_date: datetime
    return_reason: str | None = None
    handler_employee_id: int | None = None
    handler_name: str | None = None
    product_names: str | None = None
    status: str
    line_count: int
    total_return_qty: float
    remark: str | None = None
    archive_status: str | None = None
    archive_business_id: int | None = None
    archive_document_type: str | None = None
    archive_error_message: str | None = None


class ReturnItemRead(BaseModel):
    return_item_id: int
    return_order_id: int
    return_no: str
    line_no: int
    sales_order_item_id: int | None = None
    delivery_item_id: int | None = None
    delivery_no: str | None = None
    product_item_id: int
    product_code: str
    product_name: str
    source_lot_id: int | None = None
    source_lot_no: str | None = None
    source_material_lot_id: int | None = None
    source_material_lot_no: str | None = None
    source_material_code: str | None = None
    source_material_name: str | None = None
    source_material_sub_batch_no: str | None = None
    source_material_summary: str | None = None
    return_qty: float
    return_weight_kg: float
    disposition_status: str
    responsibility_type: str | None = None
    remark: str | None = None


class ReturnDispositionCreate(BaseModel):
    return_item_id: int
    disposition_type: str
    scrap_weight_kg: float = 0
    extra_cost_amount: float = 0
    scrap_sale_amount: float = 0
    remark: str | None = None


class ReturnDispositionRead(BaseModel):
    return_disposition_id: int
    disposition_no: str
    return_item_id: int
    return_no: str
    product_item_id: int | None = None
    product_code: str
    product_name: str
    return_qty: float = 0
    return_weight_kg: float = 0
    disposition_type: str
    rework_work_order_id: int | None = None
    rework_work_order_no: str | None = None
    rework_report_good_qty: float = 0
    rework_report_scrap_qty: float = 0
    rework_finished_inbound_qty: float = 0
    rework_scrap_inbound_qty: float = 0
    remaining_rework_finished_inbound_qty: float = 0
    remaining_rework_scrap_inbound_qty: float = 0
    rework_close_ready: bool = False
    rework_close_status: str | None = None
    scrap_weight_kg: float
    extra_cost_amount: float
    scrap_sale_amount: float
    closed_at: datetime | None = None
    status: str
    remark: str | None = None
    archive_status: str | None = None
    archive_business_id: int | None = None
    archive_document_type: str | None = None
    archive_error_message: str | None = None


# --- Warehouse unified inbound/outbound schemas ---

BIZ_TYPE_INBOUND = {
    "OPENING",            # 期初入库
    "CUSTOMER_SUPPLIED",  # 客料入库
    "PRODUCTION_SURPLUS", # 生产余料入库
    "OUTSOURCING_SURPLUS",# 委外余料入库
    "PRODUCTION_SCRAP_IN",# 生产废料入库
    "REWORK_SCRAP_IN",    # 返工废料入库（废料库）
    "OUTSOURCING_SCRAP_IN",# 委外废料入库
    "RETURN_IN",          # 退货入库（退货库）
    "RETURN_SCRAP_IN",    # 退货废料入库（废料库）
    "WIP_IN",             # 产中入库（半成品）
    "OUTSOURCING_IN",     # 委外入库（半成品/成品）
    "PRODUCTION_FINISHED",# 生产入库（成品）
}

BIZ_TYPE_OUTBOUND = {
    "PRODUCTION_OUT",     # 生产出库（原材料/辅料）
    "OUTSOURCING_OUT",    # 委外出库（原材料/半成品）
    "SCRAP_OUT",          # 报废出库（原材料）
    "SCRAP_SALE_OUT",     # 废料销售出库（废料库）
    "RETURN_OUT",         # 返工出库（退货库）
    "WIP_OUT",            # 产中出库（半成品）
    "SALES_OUT",          # 销售出库（成品）
}


class WarehouseInboundCreate(BaseModel):
    biz_type: str
    item_id: int
    warehouse_id: int
    location_id: int | None = None
    source_lot_id: int | None = None
    inbound_weight_kg: float = 0
    inbound_qty: float | None = 0
    unit_cost: float | None = None
    lot_no: str | None = None
    source_doc_type: str | None = None
    source_doc_id: int | None = None
    source_line_id: int | None = None
    source_lot_ids: list[int] = Field(default_factory=list)
    source_material_sub_batch_no: str | None = None
    source_material_summary: str | None = None
    provider_name: str | None = None
    waybill_no: str | None = None
    order_photo_url: str | None = None
    freight_amount: float | None = None
    remark: str | None = None
    settle_work_order: bool = False
    settle_remark: str | None = None


class OpeningInventoryImportResult(BaseModel):
    imported: int
    warehouse_type: str
    message: str


class WarehouseOutboundSourceLotCreate(BaseModel):
    source_lot_id: int = Field(gt=0)
    outbound_weight_kg: float = Field(gt=0)


class WarehouseOutboundCreate(BaseModel):
    biz_type: str
    item_id: int
    warehouse_id: int
    location_id: int | None = None
    outbound_weight_kg: float = 0
    outbound_qty: float | None = 0
    target_doc_type: str | None = None
    target_doc_id: int | None = None
    target_doc_line_id: int | None = None
    target_material_sub_batch_no: str | None = None
    sale_unit_price: float | None = None
    buyer_name: str | None = None
    sale_remark: str | None = None
    waybill_no: str | None = None
    order_photo_url: str | None = None
    freight_amount: float | None = None
    outsourcing_party_name: str | None = None
    remark: str | None = None
    source_lots: list[WarehouseOutboundSourceLotCreate] = Field(default_factory=list)


class WarehouseTransferCreate(BaseModel):
    item_id: int
    source_warehouse_id: int
    target_warehouse_id: int
    transfer_weight_kg: float
    lot_id: int | None = None
    remark: str | None = None


# --- Stocktake schemas ---


class StocktakeStartCreate(BaseModel):
    warehouse_ids: list[int] = Field(min_length=1, max_length=1)
    remark: str | None = None


class StocktakeWarehouseRead(BaseModel):
    id: int
    warehouse_id: int
    warehouse_name: str
    warehouse_type: str
    status: str


class StocktakeLineRead(BaseModel):
    id: int
    stocktake_id: int
    line_no: int
    warehouse_id: int
    warehouse_name: str
    warehouse_type: str
    location_id: int | None = None
    location_name: str | None = None
    item_id: int
    item_code: str
    item_name: str
    item_type: str
    lot_id: int | None = None
    lot_no: str | None = None
    source_material_sub_batch_no: str | None = None
    snapshot_qty: float
    snapshot_weight_kg: float
    snapshot_unit_cost: float
    counted_qty: float | None = None
    counted_weight_kg: float | None = None
    diff_qty: float
    diff_weight_kg: float
    diff_amount: float
    diff_type: str
    row_status: str
    error_message: str | None = None
    remark: str | None = None


class StocktakeRead(BaseModel):
    id: int
    stocktake_no: str
    status: str
    started_by_user_id: int | None = None
    imported_by_user_id: int | None = None
    confirmed_by_user_id: int | None = None
    canceled_by_user_id: int | None = None
    started_at: datetime
    exported_at: datetime | None = None
    imported_at: datetime | None = None
    confirmed_at: datetime | None = None
    canceled_at: datetime | None = None
    remark: str | None = None
    warehouses: list[StocktakeWarehouseRead] = Field(default_factory=list)
    summary: dict[str, float | int] = Field(default_factory=dict)


class StocktakeImportPreviewRead(BaseModel):
    stocktake: StocktakeRead
    lines: list[StocktakeLineRead]
    summary: dict[str, float | int]


class StocktakeConfirmCreate(BaseModel):
    confirm_text: str | None = None
    remark: str | None = None


# --- Special warehouse adjustment schemas ---


class SpecialWarehouseAdjustmentLineCreate(BaseModel):
    item_id: int
    lot_id: int | None = None
    location_id: int | None = None
    adjust_qty: Decimal | None = None
    adjust_weight_kg: Decimal | None = None
    unit_cost: Decimal | None = None
    line_reason: str | None = None


class SpecialWarehouseAdjustmentCreate(BaseModel):
    warehouse_id: int
    direction: str
    reason: str | None = None
    lines: list[SpecialWarehouseAdjustmentLineCreate] = Field(min_length=1)


class SpecialWarehouseAdjustmentLineRead(BaseModel):
    model_config = ConfigDict(from_attributes=True)

    id: int
    adjustment_id: int
    line_no: int
    item_id: int
    warehouse_id: int
    location_id: int | None = None
    lot_id: int
    inventory_txn_id: int | None = None
    before_qty: float
    change_qty: float
    after_qty: float
    before_weight_kg: float
    change_weight_kg: float
    after_weight_kg: float
    unit_cost: float
    line_reason: str | None = None


class SpecialWarehouseAdjustmentRead(BaseModel):
    model_config = ConfigDict(from_attributes=True)

    id: int
    adjustment_no: str
    warehouse_id: int
    warehouse_type: str
    direction: str
    reason: str
    status: str
    operator_user_id: int | None = None
    adjusted_at: datetime
    lines: list[SpecialWarehouseAdjustmentLineRead] = Field(default_factory=list)
    archive_status: str | None = None
    archive_business_id: int | None = None
    archive_document_type: str | None = None
    archive_error_message: str | None = None
