Files
zkxw/backend/app/crud/sample_info_quick_register.py
T
zlwuandCursor ac8e471aca fix: align sample registration, freight scope, and OA datetime display
Let customer-service quick register write storage, certificate, and insurance separately without extra menu or data-scope checks. List freight by document data scope like the web admin, allow protocol staff outside the collection region, and pad OA datetimes to seconds so PC date pickers can show historical minute values.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-10-20 17:30:30 +08:00

311 lines
9.7 KiB
Python

"""样本信息列表快捷登记写入。"""
from __future__ import annotations
from datetime import datetime
from typing import Any, Dict, List, Optional
from sqlalchemy.orm import Session
from app.core.utils import generate_uuid
from app.crud import sample_customer_service as sample_customer_service_crud
from app.crud import sample_insurance as sample_insurance_crud
from app.crud import sample_storage_cert as sample_storage_cert_crud
from app.crud.storage_release_review import _get_user_name
from app.services.sample_customer_service_register import has_any_customer_service_payload
from app.models.models import (
BizCollectionProtocol,
BizProtocolSample,
BizStorageReleaseReview,
)
from app.models.research_donation import BizResearchDonation, BizResearchDonationSample
from app.services.sample_info_quick_register import (
apply_quick_storage_payload,
unique_sample_items,
)
def _protocol_accessible(db: Session, protocol_id: str, tenant_no: str) -> bool:
"""登记写入只校验本租户协议存在,不再按数据权限拦截。"""
row = (
db.query(BizCollectionProtocol.id)
.filter(
BizCollectionProtocol.id == protocol_id,
BizCollectionProtocol.del_flag == 0,
BizCollectionProtocol.tenant_no == tenant_no,
)
.first()
)
return row is not None
def _donation_accessible(db: Session, donation_id: str, tenant_no: str) -> bool:
"""登记写入只校验本租户捐赠单存在,不再按数据权限拦截。"""
row = (
db.query(BizResearchDonation.id)
.filter(
BizResearchDonation.id == donation_id,
BizResearchDonation.del_flag == 0,
BizResearchDonation.tenant_no == tenant_no,
)
.first()
)
return row is not None
def _pick_latest_review(
rows: List[BizStorageReleaseReview],
) -> Optional[BizStorageReleaseReview]:
best = None
best_t = None
for rev in rows:
cur_t = rev.storage_time or rev.update_time or rev.create_time
if best is None:
best, best_t = rev, cur_t
continue
if cur_t is None:
continue
if best_t is None or cur_t > best_t:
best, best_t = rev, cur_t
return best
def batch_handle_storage_certs(
db: Session,
*,
items: List[Dict[str, str]],
tenant_no: str,
user_id: str,
handle_time: Optional[datetime],
handler_user_id: Optional[str],
attachments: str,
remark: str,
mail_cert_time: Optional[datetime] = None,
update_mail_cert_time: bool = False,
do_commit: bool = True,
) -> Dict[str, int]:
pairs = unique_sample_items(items)
if not pairs:
raise ValueError("请选择样本")
success = 0
fail = 0
for sample_code, protocol_id in pairs:
if not protocol_id or not _protocol_accessible(db, protocol_id, tenant_no):
fail += 1
continue
try:
sample_storage_cert_crud.upsert_handle(
db,
tenant_no=tenant_no,
sample_code=sample_code,
collection_protocol_id=protocol_id,
user_id=user_id,
handle_time=handle_time,
handler_user_id=handler_user_id,
cert_image_urls=attachments,
remark=remark,
mail_cert_time=mail_cert_time,
update_mail_cert_time=update_mail_cert_time,
)
success += 1
except ValueError:
fail += 1
if do_commit:
db.commit()
return {"success": success, "fail": fail}
def batch_handle_insurance(
db: Session,
*,
items: List[Dict[str, str]],
tenant_no: str,
user_id: str,
handle_time: Optional[datetime],
handler_user_id: Optional[str],
attachments: str,
remark: str,
policy_send_time: Optional[datetime] = None,
update_policy_send_time: bool = False,
do_commit: bool = True,
) -> Dict[str, int]:
pairs = unique_sample_items(items)
if not pairs:
raise ValueError("请选择样本")
success = 0
fail = 0
for sample_code, protocol_id in pairs:
if not protocol_id or not _protocol_accessible(db, protocol_id, tenant_no):
fail += 1
continue
try:
sample_insurance_crud.upsert_handle(
db,
tenant_no=tenant_no,
sample_code=sample_code,
collection_protocol_id=protocol_id,
user_id=user_id,
handle_time=handle_time,
handler_user_id=handler_user_id,
insurance_attachments=attachments,
handle_remark=remark,
policy_send_time=policy_send_time,
update_policy_send_time=update_policy_send_time,
)
success += 1
except ValueError:
fail += 1
if do_commit:
db.commit()
return {"success": success, "fail": fail}
def batch_register_customer_service(
db: Session,
*,
items: List[Dict[str, str]],
tenant_no: str,
user_id: str,
incoming: Dict[str, Any],
do_commit: bool = True,
) -> Dict[str, int]:
if not has_any_customer_service_payload(incoming):
raise ValueError("请至少填写一项客户服务信息")
pairs = unique_sample_items(items)
if not pairs:
raise ValueError("请选择样本")
success = 0
fail = 0
for sample_code, protocol_id in pairs:
if not protocol_id or not _protocol_accessible(db, protocol_id, tenant_no):
fail += 1
continue
try:
sample_customer_service_crud.upsert_by_sample_code(
db,
tenant_no=tenant_no,
sample_code=sample_code,
collection_protocol_id=protocol_id,
user_id=user_id,
incoming=incoming,
)
success += 1
except ValueError:
fail += 1
if do_commit:
db.commit()
return {"success": success, "fail": fail}
def batch_quick_register_storage(
db: Session,
*,
items: List[Dict[str, str]],
reception_source: str,
tenant_no: str,
user_id: str,
handle_time: Optional[datetime],
handler_user_id: Optional[str],
attachments: str,
remark: str,
do_commit: bool = True,
) -> Dict[str, int]:
source = (reception_source or "").strip()
if source not in ("cell_storage", "research_donation"):
raise ValueError("来源无效")
fields = apply_quick_storage_payload(
handle_time=handle_time,
handler_user_id=handler_user_id,
attachments=attachments,
remark=remark,
)
pairs = unique_sample_items(items)
if not pairs:
raise ValueError("请选择样本")
handler_name = _get_user_name(db, fields["storage_by"] or user_id)
success = 0
fail = 0
now = datetime.now()
for sample_code, protocol_id in pairs:
if source == "cell_storage":
if not protocol_id or not _protocol_accessible(db, protocol_id, tenant_no):
fail += 1
continue
else:
if not protocol_id or not _donation_accessible(db, protocol_id, tenant_no):
fail += 1
continue
rows = (
db.query(BizStorageReleaseReview)
.filter(
BizStorageReleaseReview.tenant_no == tenant_no,
BizStorageReleaseReview.del_flag == 0,
BizStorageReleaseReview.reception_source == source,
BizStorageReleaseReview.sample_code == sample_code,
)
.all()
)
rev = _pick_latest_review(rows)
if rev is None:
rev = BizStorageReleaseReview(
id=generate_uuid(),
sample_code=sample_code,
reception_source=source,
protocol_id=protocol_id,
storage_status="yrk",
tenant_no=tenant_no,
create_by=user_id,
create_dept_id="",
update_by=user_id,
create_time=now,
update_time=now,
del_flag=0,
)
db.add(rev)
rev.storage_status = fields["storage_status"]
rev.storage_time = fields["storage_time"]
rev.storage_by = fields["storage_by"]
rev.storage_by_name = handler_name
rev.remark = fields["remark"] or None
if fields["attachments"]:
rev.review_attachments = fields["attachments"]
rev.update_by = user_id
rev.update_time = now
db.add(rev)
ymd = fields["storage_in_date"]
if source == "cell_storage":
ps = (
db.query(BizProtocolSample)
.filter(
BizProtocolSample.sample_code == sample_code,
BizProtocolSample.tenant_no == tenant_no,
BizProtocolSample.del_flag == 0,
)
.first()
)
if ps:
ps.sample_status = "yrk"
ps.storage_in_date = ymd
ps.update_time = now
db.add(ps)
else:
ds = (
db.query(BizResearchDonationSample)
.filter(
BizResearchDonationSample.sample_code == sample_code,
BizResearchDonationSample.tenant_no == tenant_no,
BizResearchDonationSample.del_flag == 0,
)
.first()
)
if ds:
ds.sample_status = "yrk"
ds.storage_in_date = ymd
ds.update_time = now
db.add(ds)
success += 1
if do_commit:
db.commit()
return {"success": success, "fail": fail}