Files
shanxia2.0/backend/app/services/pl_child_sync.py
T

69 lines
2.5 KiB
Python

"""Scoped child reconciliation. Callers supply the complete writable scope, never global IDs."""
from datetime import datetime
from fastapi import HTTPException
from app.core.utils import generate_uuid
def validate_child_ids(items, existing, label):
"""Validate the whole collection before a caller mutates any ORM instance."""
by_id = {row.id: row for row in existing}
seen = set()
for item in items or []:
row_id = (getattr(item, 'id', None) or '').strip()
if not row_id:
continue
if row_id in seen:
raise HTTPException(422, f'{label} id 重复:{row_id}')
if row_id not in by_id:
raise HTTPException(422, f'{label} id 无效、已删除或不属于当前主单:{row_id}')
seen.add(row_id)
return by_id, seen
def prepare_keyed_rows(existing, values, key_fields, label):
"""Natural-key relations keep their identities; ambiguous keys must be corrected explicitly."""
old = {}
for row in existing:
key = tuple(getattr(row, field, None) or '' for field in key_fields)
if key in old:
raise HTTPException(409, f'{label}存在重复历史记录,请先核对')
old[key] = row
seen = set()
pairs = []
for value in values:
key = tuple(value.get(field) or '' for field in key_fields)
if key in seen:
raise HTTPException(422, f'{label}不能重复提交')
seen.add(key)
row = old.get(key)
if value.get('id') and (row is None or value['id'] != row.id):
raise HTTPException(422, f'{label} id 无效或不属于当前主单')
pairs.append((row, value))
return pairs, [row for key, row in old.items() if key not in seen]
def apply_keyed_rows(db, model, prepared, *, user_id, dept_id='', order_start=0):
pairs, removed = prepared
now = datetime.now()
result = []
for idx, (row, value) in enumerate(pairs, start=order_start):
fields = {key: val for key, val in value.items() if key != 'id'}
if row is None:
row = model(id=generate_uuid(), create_by=user_id, create_dept_id=dept_id or '',
create_time=now, del_flag=0, **fields)
db.add(row)
else:
for key, val in fields.items():
setattr(row, key, val)
row.order_num = idx
row.update_by = user_id
row.update_time = now
result.append(row)
for row in removed:
row.del_flag = 1
row.update_by = user_id
row.update_time = now
return result