refactor(后端): 将工资与工作区业务逻辑下沉服务层
将月份状态、资料输出、调整单编排及工作区查询、 CSV 生成移至现有服务层,路由仅绑定参数并返回响应。 保持接口路径、参数、响应模型和权限依赖一致, 补充九项内存库行为回归并同步测试记录。
This commit is contained in:
1 parent
019817d6dc
commit
71b7846a79
9 files changed
+426
-192
No files matched your search
@@ -3,17 +3,12 @@
|
||||
from typing import Annotated
|
||||
|
||||
from fastapi import APIRouter, Path, Query
|
||||
from sqlalchemy import func, select
|
||||
from starlette.responses import StreamingResponse
|
||||
|
||||
from app.api.deps import DbSession, PayrollWriter, WorkbenchReader
|
||||
from app.core.response import ApiResponse, PageQuery, PageResult, ok
|
||||
from app.models import TPlusSyncRecord, TPlusWorkLine
|
||||
from app.schemas.tplus_workbench import FactIn, SourceChoiceQuery, WorkCreateIn, WorkQuery
|
||||
from app.services.tplus import workbench
|
||||
from app.services.tplus.report_labels import ACTION_LABELS, reason_label, spreadsheet_value
|
||||
from app.services.tplus.snapshots import source_statement
|
||||
from app.services.tplus.task_service import _source_database
|
||||
|
||||
router = APIRouter()
|
||||
RunNo = Annotated[str, Path(pattern=r"^[A-Za-z0-9_-]+$", max_length=32)]
|
||||
@@ -36,33 +31,7 @@ def fact_history(
|
||||
session: DbSession, _: WorkbenchReader, fact_id: int, query: Annotated[PageQuery, Query()]
|
||||
):
|
||||
"""按事实身份分页查看修改历史,不允许跨账套读取。"""
|
||||
from app.core.exceptions import NotFoundError
|
||||
from app.models import TPlusFact
|
||||
|
||||
fact = session.get(TPlusFact, fact_id)
|
||||
if fact is None or fact.source_database != _source_database():
|
||||
raise NotFoundError("补充资料不存在")
|
||||
conditions = [
|
||||
TPlusFact.source_database == fact.source_database,
|
||||
TPlusFact.kind == fact.kind,
|
||||
TPlusFact.fact_key == fact.fact_key,
|
||||
]
|
||||
total = session.scalar(select(func.count()).select_from(TPlusFact).where(*conditions))
|
||||
rows = session.scalars(
|
||||
select(TPlusFact)
|
||||
.where(*conditions)
|
||||
.order_by(TPlusFact.version.desc())
|
||||
.offset(query.offset)
|
||||
.limit(query.page_size)
|
||||
)
|
||||
return ok(
|
||||
PageResult(
|
||||
records=[workbench.fact_out(f) for f in rows],
|
||||
total=total or 0,
|
||||
page_num=query.page_num,
|
||||
page_size=query.page_size,
|
||||
)
|
||||
)
|
||||
return ok(workbench.fact_history(session, fact_id, query))
|
||||
|
||||
|
||||
@router.post("/work-runs", response_model=ApiResponse[dict])
|
||||
@@ -98,126 +67,14 @@ def source_choices(
|
||||
query: Annotated[SourceChoiceQuery, Query()],
|
||||
):
|
||||
"""仅提供挑选资料所需的源编号和名称,不返回电话、地址和原始载荷。"""
|
||||
from app.core.exceptions import BadRequestError
|
||||
|
||||
try:
|
||||
statement = source_statement(session, _source_database(), query.entity)
|
||||
except workbench.WorkbenchError:
|
||||
raise BadRequestError("请先完整读取相关源数据") from None
|
||||
if query.search:
|
||||
escaped = query.search.replace("\\", "\\\\").replace("%", "\\%").replace("_", "\\_")
|
||||
statement = statement.where(TPlusSyncRecord.source_no.like(f"%{escaped}%", escape="\\"))
|
||||
total = session.scalar(select(func.count()).select_from(statement.subquery()))
|
||||
rows = session.scalars(
|
||||
statement.order_by(TPlusSyncRecord.source_id).offset(query.offset).limit(query.page_size)
|
||||
)
|
||||
records = [
|
||||
{
|
||||
"source_id": r.source_id,
|
||||
"source_no": r.source_no,
|
||||
"name": str(r.payload_json.get("name") or ""),
|
||||
"parent_id": str(r.payload_json.get("source_parent_id") or ""),
|
||||
"date": str(
|
||||
r.payload_json.get("voucherdate") or r.payload_json.get("voucherDate") or ""
|
||||
)[:10],
|
||||
}
|
||||
for r in rows
|
||||
]
|
||||
return ok(
|
||||
PageResult(
|
||||
records=records, total=total or 0, page_num=query.page_num, page_size=query.page_size
|
||||
)
|
||||
)
|
||||
return ok(workbench.source_choices(session, query))
|
||||
|
||||
|
||||
@router.get("/work-runs/{run_no}/report")
|
||||
def work_report(session: DbSession, _: WorkbenchReader, run_no: RunNo):
|
||||
"""导出全部预演明细,防止源单号被电子表格当作公式执行。"""
|
||||
import csv
|
||||
import io
|
||||
|
||||
workbench.get_run(session, run_no)
|
||||
|
||||
def rows():
|
||||
buffer = io.StringIO()
|
||||
writer = csv.writer(buffer)
|
||||
yield "\ufeff"
|
||||
writer.writerow(
|
||||
[
|
||||
"运行编号",
|
||||
"行号",
|
||||
"类型",
|
||||
"源ID或平账组",
|
||||
"处理结果",
|
||||
"源编号",
|
||||
"名称",
|
||||
"回款单号",
|
||||
"关联凭证示例",
|
||||
"发货单号",
|
||||
"匹配金额",
|
||||
"分配后该明细剩余应收",
|
||||
"回款天数",
|
||||
"指导成本分摊",
|
||||
"折算利润",
|
||||
"利润门槛(百分数)",
|
||||
"计提比例",
|
||||
"计提金额",
|
||||
"归属工号",
|
||||
"原因",
|
||||
"说明",
|
||||
]
|
||||
)
|
||||
yield buffer.getvalue()
|
||||
buffer.seek(0)
|
||||
buffer.truncate(0)
|
||||
statement = (
|
||||
select(TPlusWorkLine)
|
||||
.where(TPlusWorkLine.run_no == run_no)
|
||||
.order_by(TPlusWorkLine.line_no)
|
||||
)
|
||||
for row in session.scalars(statement).yield_per(200):
|
||||
data = row.data_json
|
||||
values = data.get("values") or {}
|
||||
fields = [
|
||||
run_no,
|
||||
row.line_no,
|
||||
row.entity_type,
|
||||
row.source_id,
|
||||
ACTION_LABELS.get(row.action, row.action),
|
||||
data.get("source_no", ""),
|
||||
values.get("personnel_name")
|
||||
or values.get("product_name")
|
||||
or values.get("company_name")
|
||||
or data.get("source_name")
|
||||
or "",
|
||||
*[
|
||||
data.get(k, "")
|
||||
for k in (
|
||||
"receipt_no",
|
||||
"accounting_voucher_code",
|
||||
"shipment_no",
|
||||
"amount",
|
||||
"shipment_remaining_after",
|
||||
"days",
|
||||
"allocated_cost",
|
||||
"converted_profit",
|
||||
"profit_threshold",
|
||||
"rate_percent",
|
||||
"performance",
|
||||
"personnel_id",
|
||||
)
|
||||
],
|
||||
reason_label(row.reason)
|
||||
or ("经理本人业务,已排除绩效汇总" if data.get("excluded_manager_own") else ""),
|
||||
"仅供核对,未写入正式工资",
|
||||
]
|
||||
writer.writerow([spreadsheet_value(v) for v in fields])
|
||||
yield buffer.getvalue()
|
||||
buffer.seek(0)
|
||||
buffer.truncate(0)
|
||||
|
||||
return StreamingResponse(
|
||||
rows(),
|
||||
workbench.report_csv(session, run_no),
|
||||
media_type="text/csv; charset=utf-8",
|
||||
headers={"Content-Disposition": f'attachment; filename="{run_no}-preview.csv"'},
|
||||
)
|
||||
Reference in new issue
Block a user