ForgeFlow-ERP/backend/app/schemas/operations.py
2026-06-12 16:00:56 +08:00

1757 lines
48 KiB
Python

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