feat(backend): persist and return samplePayments for market protocols

Mobile protocol create/update saves v2 per-sample payments; detail
includes samplePayments for edit hydration.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
zlwuandCursor committed 2026-10-13 15:23:24 +08:00
1 parent 9c61f36db8
commit be22c37196
2 files changed
+94

No files matched your search

+44
View File
@@ -10,6 +10,7 @@ from sqlalchemy.orm import Session
from app.core.utils import parse_datetime
from app.crud import collection_protocol as cell_crud
from app.crud import protocol_sample_payment as psp_crud
from app.crud import research_donation as donation_crud
from app.models.models import (
BizCellSampleType,
@@ -772,6 +773,37 @@ def _attach_sample_eta(
return sample_dicts
def _persist_sample_payments_if_any(
db: Session,
*,
row: BizCollectionProtocol,
body: MarketProtocolBody,
ctx: MarketFieldContext,
) -> None:
"""细胞存储:将 body.sample_payments 写入 v2 按样本缴费方案。"""
if body.source != SOURCE_CELL:
if body.sample_payments:
raise ValueError('科研捐赠不支持样本缴费方案')
return
if body.sample_payments is None:
return
version = int(getattr(row, 'payment_model_version', None) or 1)
if version < 2:
row.payment_model_version = 2
db.add(row)
db.flush()
raw = [
p.model_dump(by_alias=True, exclude_none=False) for p in body.sample_payments
]
psp_crud.save_sample_payments_for_protocol(
db,
collection_protocol_id=row.id,
tenant_no=ctx.tenant_no,
user_id=ctx.user_id,
sample_payments=raw,
)
def _cell_detail(db: Session, row: BizCollectionProtocol, tenant_no: str) -> dict:
party = cell_crud.get_party_customer(db, row.id, tenant_no)
collection = cell_crud.get_collection_info(db, row.id, tenant_no)
@@ -779,6 +811,12 @@ def _cell_detail(db: Session, row: BizCollectionProtocol, tenant_no: str) -> dic
payment = cell_crud.get_payment_by_protocol_id(db, row.id, tenant_no)
transports = cell_crud.list_sample_transports(db, row.id, tenant_no)
transport_by_sample_id = {str(t.sample_id): t for t in transports}
version = int(getattr(row, 'payment_model_version', None) or 1)
sample_payments = []
if version >= 2:
sample_payments = psp_crud.get_sample_payments_bundle(
db, collection_protocol_id=row.id, tenant_no=tenant_no
)
detail = {
'source': SOURCE_CELL,
'basic': cell_crud._protocol_to_dict(row),
@@ -791,6 +829,8 @@ def _cell_detail(db: Session, row: BizCollectionProtocol, tenant_no: str) -> dic
transport_by_sample_id,
),
'payment': cell_crud._payment_to_dict(payment) if payment else None,
'paymentModelVersion': version,
'samplePayments': sample_payments,
'attachments': {
'protocolAttachments': row.protocol_attachments or '',
'consentFormUrls': row.consent_form_urls or '',
@@ -1100,11 +1140,14 @@ def create_protocol(
create_dept_id=dept_id,
commit=False,
)
_persist_sample_payments_if_any(db, row=row, body=body, ctx=ctx)
assert_cell_protocol_access(db, row.id, ctx, user_info=user_info)
db.commit()
db.refresh(row)
return _cell_detail(db, row, ctx.tenant_no)
if body.sample_payments:
raise ValueError('科研捐赠不支持样本缴费方案')
data = {
'donation_no': body.donation_no or '',
'donation_date': body.donation_date,
@@ -1280,6 +1323,7 @@ def update_protocol(
create_dept_id=dept_id,
commit=False,
)
_persist_sample_payments_if_any(db, row=row, body=body, ctx=ctx)
row = assert_cell_protocol_access(
db, row.id, ctx, user_info=user_info
)
+50
View File
@@ -23,8 +23,10 @@ from app.models.models import (
BizProtocolPaymentDetail,
BizProtocolPaymentSchedule,
BizProtocolPaymentTransfer,
BizProtocolSamplePayment,
BizProtocolSample,
BizProtocolSampleTransport,
BizPresignOrder,
BizSampleCode,
BizSampleReception,
SysPost,
@@ -57,8 +59,10 @@ def _session():
BizProtocolSampleTransport.__table__,
BizProtocolPayment.__table__,
BizProtocolPaymentDetail.__table__,
BizProtocolSamplePayment.__table__,
BizProtocolPaymentSchedule.__table__,
BizProtocolPaymentTransfer.__table__,
BizPresignOrder.__table__,
BizSampleCode.__table__,
BizSampleReception.__table__,
BizResearchDonation.__table__,
@@ -844,6 +848,52 @@ def test_market_protocol_body_accepts_sample_payments():
assert body.sample_payments[0].pay_mode == 'dj'
def test_protocol_create_persists_sample_payments():
from app.services.market_protocol import create_protocol, get_protocol
db = _session()
_masters(db)
body = _body(
samplePayments=[
{
'sampleCode': 'S-001',
'payMode': 'dj',
'contractAmount': 20000,
'lumpSumDeadline': '20261231',
'schedules': [
{
'scheduleNo': 1,
'dueAmount': 20000,
'dueDate': '20261231',
'scheduleStatus': 'open',
'transfers': [],
}
],
}
]
)
created = create_protocol(
db, body=body, ctx=_ctx('specialist'), user_info=_user()
)
protocol_id = created['basic']['id']
pays = created.get('samplePayments') or []
assert len(pays) == 1
assert pays[0]['sampleCode'] == 'S-001'
assert pays[0]['payMode'] == 'dj'
assert float(pays[0]['contractAmount']) == 20000
assert str(pays[0]['schedules'][0].get('dueDate') or '').replace('-', '')[:8] == '20261231'
detail = get_protocol(
db,
source='cell_storage',
protocol_id=protocol_id,
ctx=_ctx('specialist'),
user_info=_user(),
)
assert len(detail.get('samplePayments') or []) == 1
assert detail['samplePayments'][0]['sampleCode'] == 'S-001'
def test_protocol_body_rejects_status_finance_and_conversion_fields():
from app.schemas.market_mobile import MarketProtocolBody