diff --git a/backend/app/api/storage_release_review.py b/backend/app/api/storage_release_review.py index 92b71744..73547945 100644 --- a/backend/app/api/storage_release_review.py +++ b/backend/app/api/storage_release_review.py @@ -44,6 +44,7 @@ async def list_storage_release_reviews( storageStatus: Optional[str] = None, reviewPhase: Optional[str] = None, qcWorkOrderCompleted: Optional[bool] = Query(None), + readyForReviewOnly: Optional[bool] = Query(None), db: Session = Depends(get_db), user: dict = Depends(require_menu_code("biz:storageReleaseReview:list")), ): @@ -60,6 +61,7 @@ async def list_storage_release_reviews( keyword=keyword, review_phase=reviewPhase, qc_work_order_completed=qcWorkOrderCompleted, + ready_for_review_only=readyForReviewOnly, user_info=user, ) return success_response(result) diff --git a/backend/app/crud/storage_release_review.py b/backend/app/crud/storage_release_review.py index 5321059c..c0d41c7a 100644 --- a/backend/app/crud/storage_release_review.py +++ b/backend/app/crud/storage_release_review.py @@ -148,6 +148,18 @@ def normalize_qc_work_order_completed(raw) -> Optional[bool]: return None +def normalize_ready_for_review_only(raw) -> Optional[bool]: + """Return True when ready-only filter should apply; None means no filter.""" + if raw is None: + return None + if isinstance(raw, bool): + return True if raw else None + s = str(raw).strip().lower() + if s in ("true", "1", "yes"): + return True + return None + + def _qc_work_order_exists_for_review_sample( tenant_no: str, *, unfinished_only: bool = False ): @@ -171,6 +183,27 @@ def _qc_work_order_exists_for_review_sample( ) +def _production_work_order_unfinished_exists_for_review_sample(tenant_no: str): + """True if sample has any unfinished inbound-side production WO (dpz/jxz).""" + conds = [ + BizProductionWorkOrder.tenant_no == tenant_no, + BizProductionWorkOrder.del_flag == 0, + BizProductionWorkOrder.sample_code.isnot(None), + BizProductionWorkOrder.sample_code != "", + eq_unicode( + BizProductionWorkOrder.sample_code, BizStorageReleaseReview.sample_code + ), + BizStorageReleaseReview.sample_code.isnot(None), + BizStorageReleaseReview.sample_code != "", + BizProductionWorkOrder.work_order_status.in_(("dpz", "jxz")), + ] + return ( + exists() + .where(and_(*conds)) + .correlate(BizStorageReleaseReview) + ) + + def list_reviews( db: Session, *, @@ -183,6 +216,7 @@ def list_reviews( keyword: Optional[str] = None, review_phase: Optional[str] = None, qc_work_order_completed=None, + ready_for_review_only=None, user_info=None, ) -> Dict[str, Any]: q0 = db.query(BizStorageReleaseReview).filter( @@ -246,6 +280,14 @@ def list_reviews( ), ) ) + if normalize_ready_for_review_only(ready_for_review_only) is True: + # 无未完成质检/生产工单 ⇒ 该侧完成(含零工单) + q0 = q0.filter( + ~_qc_work_order_exists_for_review_sample( + tenant_no, unfinished_only=True + ), + ~_production_work_order_unfinished_exists_for_review_sample(tenant_no), + ) if user_info: from app.core.data_scope import apply_data_scope q0 = apply_data_scope(q0, BizStorageReleaseReview, user_info, db, tenant_no) diff --git a/backend/tests/test_storage_release_review_ready_filter.py b/backend/tests/test_storage_release_review_ready_filter.py new file mode 100644 index 00000000..df19ed02 --- /dev/null +++ b/backend/tests/test_storage_release_review_ready_filter.py @@ -0,0 +1,21 @@ +from app.crud.storage_release_review import normalize_ready_for_review_only + + +def test_normalize_ready_for_review_only_true_values(): + assert normalize_ready_for_review_only(True) is True + assert normalize_ready_for_review_only("true") is True + assert normalize_ready_for_review_only("TRUE") is True + assert normalize_ready_for_review_only("1") is True + assert normalize_ready_for_review_only("yes") is True + assert normalize_ready_for_review_only(" YES ") is True + + +def test_normalize_ready_for_review_only_non_true_is_none(): + """Only explicit true enables the filter; false/unknown/absent → no filter.""" + assert normalize_ready_for_review_only(None) is None + assert normalize_ready_for_review_only(False) is None + assert normalize_ready_for_review_only("") is None + assert normalize_ready_for_review_only("false") is None + assert normalize_ready_for_review_only("0") is None + assert normalize_ready_for_review_only("no") is None + assert normalize_ready_for_review_only("maybe") is None