From 2124ce9208b78594ab3666ab1e6609105dd363ef Mon Sep 17 00:00:00 2001 From: zlwu <704541015@qq.com> Date: Thu, 12 Nov 2026 17:38:33 +0800 Subject: [PATCH] =?UTF-8?q?fix(lab-sample):=20=E5=89=94=E9=99=A4=E5=AF=BC?= =?UTF-8?q?=E5=87=BA=E8=87=AA=E5=AD=98=E4=BC=AA=E6=95=B0=E6=8D=AE=E5=B9=B6?= =?UTF-8?q?=E8=A1=A5=E9=BD=90=E9=97=B4=E5=85=85=E8=B4=A8=E9=87=87=E9=9B=86?= =?UTF-8?q?=E5=AD=97=E6=AE=B5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 过滤导出自存占位协议与无接收间充质行;科研捐献行从捐赠采集表取采集时间,运输温度回落到接收温度。 Co-authored-by: Cursor --- backend/app/services/lab_sample_processing.py | 47 ++++++++++++++++++- backend/tests/test_lab_sample_processing.py | 35 ++++++++++++++ 2 files changed, 81 insertions(+), 1 deletion(-) diff --git a/backend/app/services/lab_sample_processing.py b/backend/app/services/lab_sample_processing.py index 51a6f901..1f0f6ecc 100644 --- a/backend/app/services/lab_sample_processing.py +++ b/backend/app/services/lab_sample_processing.py @@ -152,6 +152,31 @@ def _yes_no_from_bool(flag: Optional[bool]) -> Optional[str]: return "是" if flag else "否" +def _donation_collection_map( + db: Session, tenant_no: str, donation_ids: List[str] +) -> Dict[str, Any]: + from app.models.research_donation import BizResearchDonationCollection + + ids = [i for i in {(x or "").strip() for x in donation_ids} if i] + if not ids: + return {} + rows = ( + db.query(BizResearchDonationCollection) + .filter( + BizResearchDonationCollection.tenant_no == tenant_no, + BizResearchDonationCollection.del_flag == 0, + BizResearchDonationCollection.research_donation_id.in_(ids), + ) + .all() + ) + out: Dict[str, Any] = {} + for row in rows: + key = str(row.research_donation_id or "").strip() + if key and key not in out: + out[key] = row + return out + + def _derive_receive_check(flags: Dict[str, bool]) -> Optional[str]: if not flags: return None @@ -722,6 +747,10 @@ def list_rows( code = sample.sample_code or "" if resolve_dataset_by_type_name(type_name, code) != dataset: continue + mother_name = ((party.mother_name if party else None) or "").strip() + # 导出联调产生的占位协议,不进入实验室处理台账 + if "导出自存" in mother_name: + continue filtered.append((sample, stype, party, coll, hospital)) extras = { @@ -841,6 +870,9 @@ def list_rows( for sample, stype, party, coll, hospital in filtered: code = sample.sample_code reception = receptions_by_sample_id.get(sample.id) or receptions_by_code.get(code) + # 间充质台账以已接收样本为准,排除无接收的占位/草稿协议 + if dataset == DATASET_MSC and reception is None: + continue production = productions.get(reception.id) if reception else None lab_code = (reception.lab_storage_code if reception else None) or "" extra = _compose_extra(lab_code, code, reception.sample_code if reception else None) @@ -1015,6 +1047,7 @@ def list_rows( ] legacy_supplements.update(_load_legacy_supplements(db, tenant_no, msc_legacy_codes)) qc_tasks_map.update(_load_qc_tasks_map(db, tenant_no, msc_legacy_codes)) + donation_coll_map = _donation_collection_map(db, tenant_no, donation_ids) for reception in msc_recs: code = (reception.lab_storage_code or reception.sample_code or "").strip() @@ -1022,7 +1055,13 @@ def list_rows( continue production = productions.get(reception.id) extra = _compose_extra(code, reception.sample_code) + # 科研捐献:运输温度缺省时用接收到样温度 + if _is_blank(extra.get("transportTempC")) and not _is_blank( + reception.receive_temperature + ): + extra["transportTempC"] = reception.receive_temperature mother = mother_by_donation.get(str(reception.research_donation_id or "")) + donation_coll = donation_coll_map.get(str(reception.research_donation_id or "")) fake_sample = SimpleNamespace( id=reception.donation_sample_id or reception.id, sample_code=reception.sample_code or code, @@ -1034,7 +1073,7 @@ def list_rows( sample=fake_sample, type_name="脐带间充质干细胞", party=fake_party, - coll=None, + coll=donation_coll, hospital=None, reception=reception, production=production, @@ -1191,6 +1230,12 @@ def _build_row( if not data.get("receiveCheck"): data["receiveCheck"] = _derive_receive_check(flags) + # 间充质运输温度:补充表为空时回落到接收到样温度 + if _is_blank(data.get("transportTempC")) and reception is not None: + temp = reception.receive_temperature + if not _is_blank(temp): + data["transportTempC"] = temp + if data.get("infectiousFourCard") is not None: data["infectiousFourCard"] = _infectious_label(data.get("infectiousFourCard")) diff --git a/backend/tests/test_lab_sample_processing.py b/backend/tests/test_lab_sample_processing.py index 8bb410aa..bd012f35 100644 --- a/backend/tests/test_lab_sample_processing.py +++ b/backend/tests/test_lab_sample_processing.py @@ -142,6 +142,41 @@ class LabSampleProcessingFieldsTest(unittest.TestCase): self.assertIn("bacteriaTest", keys) self.assertIn("mycoplasmaTest", keys) + def test_msc_donation_collect_and_transport_fallback(self): + from datetime import datetime + from app.services.lab_sample_processing import _build_row + from types import SimpleNamespace + + sample = SimpleNamespace(id="D1", sample_code="KYJZ1", collection_protocol_id=None) + party = SimpleNamespace(mother_name="孙玲") + coll = SimpleNamespace( + sample_collect_time=datetime(2026, 10, 10, 10, 5), + maternal_blood_collect_time=None, + ) + reception = SimpleNamespace( + id="R1", + reception_time=datetime(2026, 10, 10, 15, 40, 57), + receive_temperature="23.1", + handler_name="侯以婷", + remark=None, + lab_storage_code="UC2026006", + ) + row = _build_row( + dataset=DATASET_MSC, + sample=sample, + type_name="脐带间充质干细胞", + party=party, + coll=coll, + hospital=None, + reception=reception, + production=None, + extra={}, + ) + self.assertEqual(row["sampleCode"], "UC2026006") + self.assertEqual(row["collectTime"], "2026-10-10 10:05:00") + self.assertEqual(row["transportTempC"], "23.1") + self.assertNotIn("导出自存", row["motherName"] or "") + def test_legacy_supplement_fills_empty_source_fields(self): from app.services.lab_sample_processing import _build_row, _merge_extra_with_legacy from types import SimpleNamespace