"""Deletion guards for stable logistics child identities.""" import json from fastapi import HTTPException from sqlalchemy import inspect from app.models import pl_models as m def _exists(db, model, tenant_no, *filters): return db.query(model.id).filter(model.tenant_no == tenant_no, model.del_flag == 0, *filters).first() is not None def assert_stop_products_removable(db, tenant_no, plan_id, stop_type, stop_id, rows): if not rows: return ids = {row.id for row in rows} from app.models.pl_quota_models import PlStopLegalDocument if inspect(db.connection()).has_table(PlStopLegalDocument.__tablename__): # 计划锁前可能已有普通读快照;证明引用必须当前读,看到等待锁期间提交的上传。 if db.query(PlStopLegalDocument.id).filter( PlStopLegalDocument.tenant_no == tenant_no, PlStopLegalDocument.del_flag == 0, PlStopLegalDocument.plan_id == plan_id, PlStopLegalDocument.stop_type == stop_type, PlStopLegalDocument.stop_product_id.in_(ids), PlStopLegalDocument.legal_use_cert_url.isnot(None), PlStopLegalDocument.legal_use_cert_url != '', ).with_for_update().first() is not None: raise HTTPException(409, '段产品已有合法使用证明,不能移除或改变身份') if any(any(getattr(row, field, None) is not None for field in ('inbound_qty', 'inbound_price', 'inbound_amount', 'outbound_qty', 'outbound_price', 'outbound_amount')) or bool(getattr(row, 'archive_remark', None)) for row in rows): raise HTTPException(409, '段产品已有归档数据,不能移除') if _exists(db, m.PlPlanProductBrandQty, tenant_no, m.PlPlanProductBrandQty.stop_product_id.in_(ids)): raise HTTPException(409, '段产品已有归档品牌,不能移除') # Older installations may not have enabled the optional snapshot table. from app.models.pl_mobile_stop_meta import PlPlanStopProductMeta if inspect(db.connection()).has_table(PlPlanStopProductMeta.__tablename__): if _exists(db, PlPlanStopProductMeta, tenant_no, PlPlanStopProductMeta.stop_product_id.in_(ids)): raise HTTPException(409, '段产品已有业务快照,不能移除') for model in (m.PlTransportDriverStopFeedback, m.PlTransportSiteStopFeedback): feedbacks = db.query(model).filter(model.tenant_no == tenant_no, model.plan_id == plan_id, model.stop_type == stop_type, model.stop_id == stop_id, model.del_flag == 0).all() for feedback in feedbacks: try: values = json.loads(feedback.product_feedbacks or '[]') except (ValueError, TypeError): raise HTTPException(409, '停靠段回传数据无法核对,不能移除产品') if not isinstance(values, list): raise HTTPException(409, '停靠段回传数据无法核对,不能移除产品') if any(isinstance(value, dict) and (value.get('stopProductId') or value.get('stop_product_id')) in ids for value in values): raise HTTPException(409, '段产品已有司机或现场回传,不能移除') ppids = {row.plan_product_id for row in rows if row.plan_product_id} if ppids and _exists(db, m.PlTransportDbLink, tenant_no, m.PlTransportDbLink.plan_id == plan_id, m.PlTransportDbLink.stop_type == stop_type, m.PlTransportDbLink.stop_id == stop_id, m.PlTransportDbLink.plan_product_id.in_(ppids)): raise HTTPException(409, '段产品已关联地磅单,不能移除') def assert_stop_removable(db, tenant_no, plan_id, stop_type, stop_id, rows): assert_stop_products_removable(db, tenant_no, plan_id, stop_type, stop_id, rows) for model in (m.PlTransportDriverStopFeedback, m.PlTransportSiteStopFeedback, m.PlTransportDbLink): if _exists(db, model, tenant_no, model.plan_id == plan_id, model.stop_type == stop_type, model.stop_id == stop_id): raise HTTPException(409, '停靠段已有回传或地磅关联,不能移除') def assert_plan_products_removable(db, tenant_no, plan_id, rows, ignored_stop_ids=None): if not rows: return ids = {row.id for row in rows} for model in (m.PlPlanLoadProduct, m.PlPlanDeliveryProduct, m.PlPlanProductBrandQty, m.PlInboundOrderItem, m.PlOutboundOrderItem, m.PlTransportProductFeedback, m.PlTransportProductBrandQty, m.PlTransportDbLink, m.PlTransportSiteLink): filters = [model.plan_product_id.in_(ids)] if model in (m.PlPlanLoadProduct, m.PlPlanDeliveryProduct) and ignored_stop_ids: filters.append(~model.id.in_(ignored_stop_ids)) if _exists(db, model, tenant_no, *filters): raise HTTPException(409, '计划产品已有段产品、归档、回传或出入库关联,不能移除') if any(any(getattr(row, name, None) is not None for name in ('inbound_qty', 'outbound_qty')) for row in rows): raise HTTPException(409, '计划产品已有归档数量,不能移除') product_ids = {row.product_id for row in rows} if _exists(db, m.PlInventoryTransaction, tenant_no, m.PlInventoryTransaction.plan_id == plan_id, m.PlInventoryTransaction.product_id.in_(product_ids)): raise HTTPException(409, '计划产品已有库存流水,不能移除')