Просмотр исходного кода

feat(backend): accept samplePayments on market protocol body

Whitelist per-sample payment payload for mobile protocol create/update.

Co-authored-by: Cursor <cursoragent@cursor.com>
zlwu 3 недель назад
Родитель
Сommit
9c61f36db8
2 измененных файлов с 71 добавлено и 0 удалено
  1. 40 0
      backend/app/schemas/market_mobile.py
  2. 31 0
      backend/tests/test_market_protocol_api.py

+ 40 - 0
backend/app/schemas/market_mobile.py

@@ -247,6 +247,43 @@ class MarketProtocolAttachmentsBody(BaseModel):
     )
 
 
+class MarketProtocolPaymentScheduleBody(BaseModel):
+    model_config = ConfigDict(extra='forbid', populate_by_name=True)
+
+    id: Optional[str] = None
+    schedule_no: Optional[int] = Field(None, alias='scheduleNo')
+    due_amount: Optional[float] = Field(None, alias='dueAmount')
+    due_date: Optional[str] = Field(None, alias='dueDate')
+    schedule_status: Optional[str] = Field(None, alias='scheduleStatus')
+    transfers: Optional[List[dict]] = None
+
+
+class MarketProtocolSamplePaymentBody(BaseModel):
+    model_config = ConfigDict(extra='forbid', populate_by_name=True)
+
+    id: Optional[str] = None
+    sample_code: str = Field(..., min_length=1, alias='sampleCode')
+    protocol_sample_id: Optional[str] = Field(None, alias='protocolSampleId')
+    pay_mode: str = Field(..., alias='payMode')
+    contract_amount: Optional[float] = Field(None, alias='contractAmount')
+    lump_sum_deadline: Optional[str] = Field(None, alias='lumpSumDeadline')
+    installment_count: Optional[int] = Field(None, alias='installmentCount')
+    installment_interval_months: Optional[float] = Field(
+        None, alias='installmentIntervalMonths'
+    )
+    installment_start_date: Optional[str] = Field(
+        None, alias='installmentStartDate'
+    )
+    installment_advance_days: Optional[int] = Field(
+        None, alias='installmentAdvanceDays'
+    )
+    installment_ratios: Optional[List[float]] = Field(
+        None, alias='installmentRatios'
+    )
+    remark: Optional[str] = None
+    schedules: Optional[List[MarketProtocolPaymentScheduleBody]] = None
+
+
 class MarketProtocolBody(BaseModel):
     """移动协议白名单;状态、审核、财务、退款及转换字段一律拒绝。"""
 
@@ -266,6 +303,9 @@ class MarketProtocolBody(BaseModel):
     collection: Optional[MarketProtocolCollectionBody] = None
     samples: Optional[List[MarketProtocolSampleBody]] = None
     attachments: Optional[MarketProtocolAttachmentsBody] = None
+    sample_payments: Optional[List[MarketProtocolSamplePaymentBody]] = Field(
+        None, alias='samplePayments'
+    )
 
 
 class MarketProtocolAuditBody(BaseModel):

+ 31 - 0
backend/tests/test_market_protocol_api.py

@@ -813,6 +813,37 @@ def test_protocol_create_forbids_existing_customer_ids(source):
         )
 
 
+def test_market_protocol_body_accepts_sample_payments():
+    from app.schemas.market_mobile import MarketProtocolBody
+
+    body = MarketProtocolBody.model_validate(
+        {
+            'source': 'cell_storage',
+            'protocolNo': 'P-PAY-1',
+            'customer': {'motherName': '李四'},
+            'samplePayments': [
+                {
+                    'sampleCode': 'S-001',
+                    'payMode': 'dj',
+                    'contractAmount': 20000,
+                    'lumpSumDeadline': '20261231',
+                    'schedules': [
+                        {
+                            'scheduleNo': 1,
+                            'dueAmount': 20000,
+                            'dueDate': '20261231',
+                        }
+                    ],
+                }
+            ],
+        }
+    )
+    assert body.sample_payments is not None
+    assert len(body.sample_payments) == 1
+    assert body.sample_payments[0].sample_code == 'S-001'
+    assert body.sample_payments[0].pay_mode == 'dj'
+
+
 def test_protocol_body_rejects_status_finance_and_conversion_fields():
     from app.schemas.market_mobile import MarketProtocolBody