136 lines
5.3 KiB
Python
136 lines
5.3 KiB
Python
import re
|
|
from typing import Any
|
|
|
|
from pydantic import BaseModel, Field, model_validator
|
|
|
|
from app.schemas.common import NaiveDatetimeOptional
|
|
|
|
|
|
_EMAIL_REGEX = re.compile(r"^[\w.\-]+@[\w.\-]+\.\w+$")
|
|
|
|
|
|
class InvoiceCreateRequest(BaseModel):
|
|
"""创建发票请求。"""
|
|
header_type: str = Field(..., pattern="^(personal|company)$", description="抬头类型")
|
|
header_name: str = Field(..., min_length=1, max_length=128, description="抬头名称")
|
|
header_tax_no: str | None = Field(None, max_length=32, description="税号")
|
|
header_register_address: str | None = Field(None, max_length=256, description="注册地址")
|
|
header_register_phone: str | None = Field(None, max_length=32, description="注册电话")
|
|
header_bank_name: str | None = Field(None, max_length=128, description="开户行")
|
|
header_bank_account: str | None = Field(None, max_length=64, description="银行账号")
|
|
email: str = Field(..., max_length=128, description="电子邮箱(必填)")
|
|
order_ids: list[str] = Field(..., min_length=1, description="订单ID列表")
|
|
|
|
@model_validator(mode="after")
|
|
def validate_email(self) -> "InvoiceCreateRequest":
|
|
if not _EMAIL_REGEX.match(self.email):
|
|
raise ValueError("邮箱格式不正确")
|
|
return self
|
|
|
|
@model_validator(mode="after")
|
|
def validate_company_fields(self) -> "InvoiceCreateRequest":
|
|
if self.header_type == "company" and not self.header_tax_no:
|
|
raise ValueError("企业抬头必须填写税号")
|
|
return self
|
|
|
|
|
|
class InvoiceStatusUpdateRequest(BaseModel):
|
|
"""更新发票状态请求。"""
|
|
status: str = Field(..., pattern="^(success|failed)$", description="目标状态")
|
|
failure_reason: str | None = Field(None, max_length=500, description="失败原因")
|
|
|
|
@model_validator(mode="after")
|
|
def validate_failure_reason(self) -> "InvoiceStatusUpdateRequest":
|
|
if self.status == "failed" and not self.failure_reason:
|
|
raise ValueError("开具失败时必须填写失败原因")
|
|
return self
|
|
|
|
|
|
class InvoiceOrderOut(BaseModel):
|
|
"""发票关联订单响应。"""
|
|
model_config = {"from_attributes": True}
|
|
|
|
id: str
|
|
invoice_id: str
|
|
order_id: str
|
|
order_no: str
|
|
amount: float
|
|
credits: float
|
|
|
|
|
|
class InvoiceOut(BaseModel):
|
|
"""发票响应体。"""
|
|
model_config = {"from_attributes": True}
|
|
|
|
id: str
|
|
user_id: str
|
|
invoice_no: str
|
|
header_type: str
|
|
header_name: str
|
|
header_tax_no: str | None = None
|
|
header_register_address: str | None = None
|
|
header_register_phone: str | None = None
|
|
header_bank_name: str | None = None
|
|
header_bank_account: str | None = None
|
|
email: str
|
|
total_amount: float
|
|
total_credits: float
|
|
status: str
|
|
failure_reason: str | None = None
|
|
issued_at: NaiveDatetimeOptional = None
|
|
created_at: NaiveDatetimeOptional = None
|
|
updated_at: NaiveDatetimeOptional = None
|
|
orders: list[InvoiceOrderOut] = []
|
|
|
|
|
|
# ── 发票抬头 ──────────────────────────────────────────────
|
|
|
|
class InvoiceHeaderCreate(BaseModel):
|
|
"""创建发票抬头请求。"""
|
|
type: str = Field(..., pattern="^(personal|company)$", description="抬头类型")
|
|
name: str = Field(..., min_length=1, max_length=128, description="抬头名称")
|
|
tax_no: str | None = Field(None, max_length=32, description="税号")
|
|
register_address: str | None = Field(None, max_length=256, description="注册地址")
|
|
register_phone: str | None = Field(None, max_length=32, description="注册电话")
|
|
bank_name: str | None = Field(None, max_length=128, description="开户行")
|
|
bank_account: str | None = Field(None, max_length=64, description="银行账号")
|
|
email: str | None = Field(None, max_length=128, description="接收邮箱")
|
|
is_default: bool = Field(False, description="是否设为默认")
|
|
|
|
@model_validator(mode="after")
|
|
def validate_company_fields(self) -> "InvoiceHeaderCreate":
|
|
if self.type == "company" and not self.tax_no:
|
|
raise ValueError("企业抬头必须填写税号")
|
|
return self
|
|
|
|
|
|
class InvoiceHeaderUpdate(BaseModel):
|
|
"""更新发票抬头请求。"""
|
|
name: str | None = Field(None, min_length=1, max_length=128, description="抬头名称")
|
|
tax_no: str | None = Field(None, max_length=32, description="税号")
|
|
register_address: str | None = Field(None, max_length=256, description="注册地址")
|
|
register_phone: str | None = Field(None, max_length=32, description="注册电话")
|
|
bank_name: str | None = Field(None, max_length=128, description="开户行")
|
|
bank_account: str | None = Field(None, max_length=64, description="银行账号")
|
|
email: str | None = Field(None, max_length=128, description="接收邮箱")
|
|
is_default: bool | None = Field(None, description="是否设为默认")
|
|
|
|
|
|
class InvoiceHeaderOut(BaseModel):
|
|
"""发票抬头响应体。"""
|
|
model_config = {"from_attributes": True}
|
|
|
|
id: str
|
|
user_id: str
|
|
type: str
|
|
name: str
|
|
tax_no: str | None = None
|
|
register_address: str | None = None
|
|
register_phone: str | None = None
|
|
bank_name: str | None = None
|
|
bank_account: str | None = None
|
|
email: str | None = None
|
|
is_default: bool = False
|
|
created_at: NaiveDatetimeOptional = None
|
|
updated_at: NaiveDatetimeOptional = None
|