ci/woodpecker/push/main Pipeline failed
- ① kpi_templates instantiate 构造点显式传会话账套(原缺 entity_id ⇒ 静默落 e1),kpi_code 判重按账套收紧
- ② kpi_subject_map 3 个读取点加护栏注释(不加列、不改语义)
- ③ subjects 分层:读口 union(NULL=全局标准准则科目 / N=账套私有),UPSERT 唯一键改 (entity_id, subject_code)
- ④ analysis_results 账套化:读/写/删三端点按会话账套判定归属(列未落地时 kpi_code JOIN 止血),
无归属行进 unattributed 登记、删除一律 404
- 模型层按现网 DDL 补齐:KPIDefinition(template_id/is_system + uk_entity_kpi)、AnalysisResult.entity_id、Subject.entity_id
- 新增 migrate_{subjects,analysis_results}_entity_id_20260923.py(--check/--apply/--down)、
22 条对账用例 test_entity_dimension_convergence_20260923.py
DDL 已由 ops 受控通道落地(subjects 21:31:56 / analysis_results 21:31:59 CST,审批 1e9b4d9a/5ffb1640)
本提交为切片提交,未 push(发布须另取放行)
107 lines
4.1 KiB
Python
107 lines
4.1 KiB
Python
"""会计科目管理 — 新30号准则适配"""
|
||
from fastapi import APIRouter, Depends, HTTPException, Query
|
||
from sqlalchemy.orm import Session
|
||
from sqlalchemy import func
|
||
from typing import Optional, List
|
||
from app.database import get_db
|
||
from app.auth_middleware import require_role
|
||
from app.deps import get_entity_id
|
||
from app.models import Subject
|
||
import logging
|
||
|
||
logger = logging.getLogger("cma.subjects")
|
||
|
||
router = APIRouter(prefix="/api/cma/subjects", tags=["会计科目"],
|
||
dependencies=[Depends(require_role("ceo", "finance", "business"))],
|
||
)
|
||
|
||
|
||
@router.get("")
|
||
def list_subjects(
|
||
category: Optional[str] = Query(None, description="新30号准则分类过滤"),
|
||
keyword: Optional[str] = Query(None, description="科目名称/编码搜索"),
|
||
db: Session = Depends(get_db),
|
||
entity_id: int = Depends(get_entity_id),
|
||
):
|
||
"""科目列表 — 支持新30号准则分类筛选
|
||
|
||
账套分层(2026-09-23 `subjects` 加 `entity_id` 列):返回**全局标准准则科目**
|
||
(`entity_id IS NULL`)∪ **本账套私有明细**(`entity_id = 会话账套`)。
|
||
行为变更(预期):博海会话不再看到酣客智享通明细(`100106 酒窖备用金` / `100108 友赞商户` 等)。
|
||
返回体字段不删不加必填(前端契约不变)。
|
||
"""
|
||
query = db.query(Subject).filter(
|
||
Subject.is_active == 1,
|
||
(Subject.entity_id.is_(None)) | (Subject.entity_id == entity_id), # 分层读取口径
|
||
)
|
||
|
||
if category:
|
||
query = query.filter(Subject.new_standard_category == category)
|
||
|
||
if keyword:
|
||
like = f"%{keyword}%"
|
||
query = query.filter(
|
||
Subject.subject_name.like(like) | Subject.subject_code.like(like)
|
||
)
|
||
|
||
subjects = query.order_by(Subject.subject_code).all()
|
||
|
||
return {
|
||
"total": len(subjects),
|
||
"data": [
|
||
{
|
||
"id": s.id,
|
||
"subject_code": s.subject_code,
|
||
"subject_name": s.subject_name,
|
||
"parent_code": s.parent_code,
|
||
"level": s.level,
|
||
"category": s.category,
|
||
"new_standard_category": s.new_standard_category,
|
||
"is_active": s.is_active,
|
||
"remark": s.remark,
|
||
"updated_at": s.updated_at.isoformat() if s.updated_at else None,
|
||
}
|
||
for s in subjects
|
||
],
|
||
}
|
||
|
||
|
||
@router.put("/{subject_id}")
|
||
def update_subject_category(
|
||
subject_id: int,
|
||
new_standard_category: str = Query(..., description="operating/investing/financing/tax/discontinued"),
|
||
db: Session = Depends(get_db),
|
||
):
|
||
"""更新单个科目的新30号准则分类"""
|
||
subject = db.query(Subject).filter(Subject.id == subject_id).first()
|
||
if not subject:
|
||
raise HTTPException(status_code=404, detail="科目不存在")
|
||
|
||
valid = {"operating", "operating_rd", "operating_fx", "investing",
|
||
"financing", "financing_fx", "tax", "discontinued"}
|
||
if new_standard_category not in valid:
|
||
raise HTTPException(status_code=400, detail=f"无效的分类: {new_standard_category}")
|
||
|
||
subject.new_standard_category = new_standard_category
|
||
db.commit()
|
||
return {"message": "更新成功", "subject_id": subject_id, "new_standard_category": new_standard_category}
|
||
|
||
|
||
@router.put("/batch/category")
|
||
def batch_update_category(
|
||
ids: List[int] = Query(..., description="科目ID列表"),
|
||
new_standard_category: str = Query(..., description="operating/investing/financing/tax/discontinued"),
|
||
db: Session = Depends(get_db),
|
||
):
|
||
"""批量更新科目新30号准则分类"""
|
||
valid = {"operating", "operating_rd", "operating_fx", "investing",
|
||
"financing", "financing_fx", "tax", "discontinued"}
|
||
if new_standard_category not in valid:
|
||
raise HTTPException(status_code=400, detail=f"无效的分类: {new_standard_category}")
|
||
|
||
updated = db.query(Subject).filter(
|
||
Subject.id.in_(ids), Subject.is_active == 1
|
||
).update({"new_standard_category": new_standard_category}, synchronize_session=False)
|
||
db.commit()
|
||
return {"message": f"批量更新成功", "updated_count": updated}
|