122 lines
5.4 KiB
Python
122 lines
5.4 KiB
Python
"""证件档案写入契约:只接收可写字段,遗漏与清空严格区分。"""
|
|
from datetime import date
|
|
from decimal import Decimal
|
|
from typing import List, Literal, Optional
|
|
from pydantic import BaseModel, ConfigDict, Field, field_validator
|
|
|
|
|
|
def camel(name: str) -> str:
|
|
first, *parts = name.split('_')
|
|
return first + ''.join(p.title() for p in parts)
|
|
|
|
|
|
class InputBody(BaseModel):
|
|
model_config = ConfigDict(alias_generator=camel, populate_by_name=True, extra='forbid')
|
|
|
|
|
|
class CertFields(InputBody):
|
|
cert_no: Optional[str] = Field(None, min_length=1, max_length=100)
|
|
issue_date: Optional[date] = None
|
|
product_name_on_cert: Optional[str] = Field(None, min_length=1, max_length=200)
|
|
total_qty: Optional[Decimal] = Field(None, ge=0, max_digits=18, decimal_places=6, allow_inf_nan=False)
|
|
quantity_unit: Optional[str] = Field(None, min_length=1, max_length=32)
|
|
valid_from: Optional[date] = None
|
|
valid_to: Optional[date] = None
|
|
valid_use_text: Optional[str] = Field(None, min_length=1, max_length=32)
|
|
entry_mode: Optional[Literal['manual', 'image_parse']] = None
|
|
attachment_urls: Optional[str] = None
|
|
status: Optional[Literal['draft', 'suspended', 'void']] = None
|
|
remark: Optional[str] = Field(None, max_length=1000)
|
|
order_num: Optional[int] = None
|
|
|
|
|
|
@field_validator('valid_use_text')
|
|
@classmethod
|
|
def validate_use_text(cls, value):
|
|
if value is not None and not value.strip():
|
|
raise ValueError('有效次数原文不能为空')
|
|
return value
|
|
|
|
|
|
class PurchaseCertUpdate(CertFields):
|
|
buyer_name: Optional[str] = Field(None, max_length=255)
|
|
buyer_address: Optional[str] = Field(None, max_length=1000)
|
|
buyer_legal_representative: Optional[str] = Field(None, max_length=100)
|
|
buyer_phone: Optional[str] = Field(None, max_length=100)
|
|
seller_name: Optional[str] = Field(None, max_length=255)
|
|
seller_address: Optional[str] = Field(None, max_length=1000)
|
|
seller_legal_representative: Optional[str] = Field(None, max_length=100)
|
|
seller_phone: Optional[str] = Field(None, max_length=100)
|
|
purchase_purpose: Optional[str] = Field(None, max_length=1000)
|
|
|
|
|
|
class PurchaseCertCreate(PurchaseCertUpdate):
|
|
cert_no: str = Field(min_length=1, max_length=100)
|
|
product_name_on_cert: str = Field(min_length=1, max_length=200)
|
|
total_qty: Decimal = Field(ge=0, max_digits=18, decimal_places=6, allow_inf_nan=False)
|
|
quantity_unit: str = Field(min_length=1, max_length=32)
|
|
valid_use_text: str = Field(min_length=1, max_length=32)
|
|
buyer_name: str = Field(min_length=1, max_length=255)
|
|
seller_name: str = Field(min_length=1, max_length=255)
|
|
entry_mode: Literal['manual', 'image_parse'] = 'manual'
|
|
|
|
|
|
class PurchaseReference(InputBody):
|
|
id: Optional[str] = None
|
|
reference_type: Literal['purchase_permit', 'purchase_filing', 'export_permit', 'unknown']
|
|
reference_no: str = Field(min_length=1, max_length=100)
|
|
purchase_cert_id: Optional[str] = None
|
|
remark: Optional[str] = Field(None, max_length=1000)
|
|
order_num: Optional[int] = None
|
|
|
|
|
|
class TransportCertUpdate(CertFields):
|
|
shipper_name: Optional[str] = Field(None, max_length=255)
|
|
shipper_address: Optional[str] = Field(None, max_length=1000)
|
|
shipper_legal_representative: Optional[str] = Field(None, max_length=100)
|
|
shipper_phone: Optional[str] = Field(None, max_length=100)
|
|
packaging_text: Optional[str] = Field(None, max_length=255)
|
|
carrier_name: Optional[str] = Field(None, max_length=255)
|
|
carrier_address_phone: Optional[str] = Field(None, max_length=1200)
|
|
transport_mode: Optional[str] = Field(None, max_length=100)
|
|
vehicle_numbers_json: Optional[List[str]] = None
|
|
route_text: Optional[str] = None
|
|
consignee_name: Optional[str] = Field(None, max_length=255)
|
|
applicant_name: Optional[str] = Field(None, max_length=255)
|
|
purchase_refs: Optional[List[PurchaseReference]] = None
|
|
|
|
|
|
class TransportCertCreate(TransportCertUpdate):
|
|
cert_no: str = Field(min_length=1, max_length=100)
|
|
product_name_on_cert: str = Field(min_length=1, max_length=200)
|
|
total_qty: Decimal = Field(ge=0, max_digits=18, decimal_places=6, allow_inf_nan=False)
|
|
quantity_unit: str = Field(min_length=1, max_length=32)
|
|
valid_use_text: str = Field(min_length=1, max_length=32)
|
|
shipper_name: str = Field(min_length=1, max_length=255)
|
|
vehicle_numbers_json: List[str] = Field(default_factory=list)
|
|
entry_mode: Literal['manual', 'image_parse'] = 'manual'
|
|
|
|
|
|
class CertConfirmBody(InputBody):
|
|
operation_id: str = Field(min_length=1, max_length=64)
|
|
used_qty: Decimal = Field(ge=0, max_digits=18, decimal_places=6, allow_inf_nan=False)
|
|
single_use_consumed: bool
|
|
reason: str = Field(min_length=1, max_length=1000)
|
|
evidence_urls: Optional[str] = None
|
|
source_note: Optional[str] = Field(None, max_length=1000)
|
|
|
|
|
|
class CertVerifyBody(InputBody):
|
|
operation_id: str = Field(min_length=1, max_length=64)
|
|
reason: str = Field(min_length=1, max_length=1000)
|
|
evidence_urls: Optional[str] = None
|
|
source_note: Optional[str] = Field(None, max_length=1000)
|
|
|
|
|
|
class CertTotalAdjustBody(InputBody):
|
|
operation_id: str = Field(min_length=1, max_length=64)
|
|
total_qty: Decimal = Field(ge=0, max_digits=18, decimal_places=6, allow_inf_nan=False)
|
|
reason: str = Field(min_length=1, max_length=1000)
|
|
source_note: str = Field('', max_length=1000)
|
|
evidence_urls: str = Field('', max_length=2000)
|