fix(borrow): 借还记录「归还人」列错标成了经手库管,拆成两列
问题
----
列表里那一列标着「归还人」,读的却是 trans_borrow.return_operator —— 而该字段
存的是**办理还库的库管**,不是来还东西的人。两者本就是不同的人:
· returner_id(trans_borrow_return)—— 把东西交回窗口的人,已校验 == 当时持有人
· return_operator —— 经手办理的库管
实测(借用行 120):return_operator = 杜邢宸/duxingchen(库管),
而实际归还人是 returner_id = 21(测试)—— 页面却显示成了「杜邢宸」。
改动
----
一、后端 get_records 增补 returners:从 trans_borrow_return 取 returner_id 并
反查 sys_user 得到姓名(去重按明细挂回)。批量查一次,不做 N+1。
二、前端拆成两列,各自名副其实:
「归还人」 ← returners(后端新增)
「经手库管」 ← return_operators(原列改为正确标签)
三、顺带修展示口径:return_operator 存的是**完整 username**(高闯/gaochuang),
未按全站口径截断。_display_borrow_operator 现统一归一到展示名
(数字 id 反查 / 姓名、斜杠前段 / 已是展示名 原样),并修正其 docstring
—— 它原本也把该字段称作「归还人」,是同一个误解的源头。
★ 历史数据的现实
实际归还人流水是二期才建的,**历史归还没有这个记录**。这部分行的「归还人」
显示为空并挂 tooltip 说明「该笔归还发生在实际归还人记录上线之前」——
刻意不拿库管的名字顶上,那正是本次要修的错。
验证
BOR-20260918-0001 → 归还人=['测试']、经手库管='杜邢宸' ✓ 两者分开
BOR-20260914-0001 → 归还人=[](历史)、经手库管='高闯' ✓
归一化:'高闯/gaochuang'→'高闯'、'21'→'测试' ✓
前端 vite build 通过;本次无需 DB 迁移。
This commit is contained in:
@ -16,11 +16,23 @@ def _borrow_user_name(uid):
|
||||
|
||||
|
||||
def _display_borrow_operator(op):
|
||||
"""归还人展示映射:历史存的是数字 user id → 映射为用户名;新数据存姓名则原样返回"""
|
||||
if op and str(op).strip().isdigit():
|
||||
mapped = _borrow_user_name(int(op))
|
||||
return mapped if mapped else op
|
||||
return op
|
||||
"""
|
||||
**经手库管**的展示映射(注意:不是「归还人」—— 两者是不同的人)。
|
||||
|
||||
该字段历史上存过三种口径,统一归一到「展示名」:
|
||||
· 数字 user id(更早期) → 反查 sys_user 取姓名;
|
||||
· 完整 username「姓名/拼音」 → 取斜杠前段(与全站展示口径一致);
|
||||
· 已经是展示名 → 原样返回。
|
||||
"""
|
||||
if not op:
|
||||
return op
|
||||
s = str(op).strip()
|
||||
if s.isdigit():
|
||||
mapped = _borrow_user_name(int(s))
|
||||
if mapped:
|
||||
return mapped.split('/')[0] if '/' in mapped else mapped
|
||||
return op
|
||||
return s.split('/')[0] if '/' in s else op
|
||||
|
||||
|
||||
class TransBorrow(db.Model):
|
||||
|
||||
@ -1586,6 +1586,35 @@ class TransService:
|
||||
TransBorrowTransfer.status == TRANSFER_STATUS_PENDING,
|
||||
).all() if _ids else []
|
||||
_pending_map = {t.borrow_id: t.to_dict() for t in _pending}
|
||||
# ============================================================
|
||||
# ★ 实际归还人(trans_borrow_return.returner_id)
|
||||
#
|
||||
# 与主表的 return_operator(**经手库管**)是**两个不同的人**:
|
||||
# · returner_id —— 把东西交回窗口的人(已校验 == 当时持有人)
|
||||
# · return_operator —— 办理还库的库管
|
||||
# 列表原先把后者标成「归还人」展示,属标签错误;此处补上真正
|
||||
# 的归还人,供前端分列展示。
|
||||
# ⚠ 本表是二期才建的,**历史归还没有这个记录** —— 那部分行的
|
||||
# returners 为空,前端显示为空并提示「历史数据未记录」,
|
||||
# 而不是拿库管的名字顶上(那正是本次要修的错)。
|
||||
# ============================================================
|
||||
_ret_rows = TransBorrowReturn.query.filter(
|
||||
TransBorrowReturn.borrow_id.in_(_ids)
|
||||
).all() if _ids else []
|
||||
_ret_uids = {t.returner_id for t in _ret_rows if t.returner_id}
|
||||
_ret_names = {}
|
||||
if _ret_uids:
|
||||
from app.models.system import SysUser
|
||||
for _u in SysUser.query.filter(SysUser.id.in_(_ret_uids)).all():
|
||||
_ret_names[_u.id] = user_display_name(_u)
|
||||
_ret_map = {}
|
||||
for _t in _ret_rows:
|
||||
_nm = _ret_names.get(_t.returner_id)
|
||||
if _nm:
|
||||
_ret_map.setdefault(_t.borrow_id, set()).add(_nm)
|
||||
for d in items_with_names:
|
||||
d['returners'] = sorted(_ret_map.get(d.get('id'), set()))
|
||||
|
||||
for d in items_with_names:
|
||||
_pt = _pending_map.get(d.get('id'))
|
||||
if _pt is not None:
|
||||
|
||||
@ -209,9 +209,29 @@
|
||||
<el-tag type="info">{{ row.children ? row.children.length : 0 }} 项</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<!-- ★ 归还人改为整单汇总:主行原取「首条明细」的归还人,多明细分批归还时
|
||||
会只显示其中一位,令人误以为其他人没还过。 -->
|
||||
<el-table-column v-if="hasColumnPermission('return_operator')" label="归还人" min-width="90">
|
||||
<!-- ★ 「归还人」与「经手库管」是**两个不同的人**,必须分列:
|
||||
returners ← trans_borrow_return.returner_id(把东西交回窗口的人)
|
||||
return_operators ← trans_borrow.return_operator(办理还库的库管)
|
||||
原先只有一列,标着「归还人」读的却是 return_operator —— 展示成了库管。
|
||||
⚠ 实际归还人流水是二期才建的,**历史归还查不到**:此时列显示为空并给出
|
||||
提示,而不是拿库管的名字顶上(那正是本次要修的错)。 -->
|
||||
<el-table-column label="归还人" min-width="90">
|
||||
<template #default="{row}">
|
||||
<template v-if="row.returners && row.returners.length">
|
||||
<span v-for="n in row.returners" :key="n" class="name-fixed">{{ formatName(n) }}</span>
|
||||
</template>
|
||||
<el-tooltip
|
||||
v-else-if="row.status === 'returned' || row.status === 'scrapped'"
|
||||
placement="top"
|
||||
content="该笔归还发生在「实际归还人」记录上线之前,系统当时只留了经手库管"
|
||||
>
|
||||
<span class="text-info">—</span>
|
||||
</el-tooltip>
|
||||
<span v-else class="text-info">—</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<!-- 经手库管:办理还库的库管,与「归还人」不是同一人,故单列展示 -->
|
||||
<el-table-column v-if="hasColumnPermission('return_operator')" label="经手库管" min-width="90">
|
||||
<template #default="{row}">
|
||||
<template v-if="row.return_operators && row.return_operators.length">
|
||||
<span v-for="op in row.return_operators" :key="op" class="name-fixed">{{ formatName(op) }}</span>
|
||||
|
||||
Reference in New Issue
Block a user