feat(return): 退回流水看板接口与权限收口

新增只读台账接口:
- GET /api/v1/outbound/returns  退回流水(分页 + 关键词 + 类型 + 时间过滤)
  返回 原出库单号 / 物料名称 / 规格 / SKU / 退回类型 / 退回数量 / 原因 /
  操作人 / 退回时间 / 公司。出库单号经 trans_outbound 批量补齐,物料名按
  多态来源批量解析,均为批量查询无 N+1。

权限收口(配合 db_migrations 里的三个权限码):
- return-from-outbound   inventory_stocktake:operation -> outbound_return
- GET /stock/defective   inventory_stocktake           -> defective_list
- restock                inventory_stocktake:operation -> defective_restock
- scrap                  inventory_stocktake:operation -> defective_scrap
- change-status          inventory_stocktake:operation -> stock_change_status

  原先这四个接口搭的是「盲盘作业」权限的便车,职责错配、审计不合规。
  实测 SALES(销售)角色持有 inventory_stocktake,意味着销售人员能读整份
  不良品台账——与业务对台账可见性的要求不符。全部改用无冒号专用码后,
  实测「只授予 inventory_stocktake:operation」对四个接口均返回 403,便车已封。

trans_return 补 company_name 快照:
  退回流水的隔离判定原先只能靠 join 链推,而库存行会被入库模块物理删除
  (实测 1077 条出库记录中已有 7 条悬空),链路一断记录就会对普通用户
  静默消失。改由退回时落快照,隔离不再依赖任何 join。
This commit is contained in:
yueli
2026-09-16 16:45:52 +08:00
parent a252013573
commit 9eb4792d4a
3 changed files with 187 additions and 5 deletions

View File

@ -268,6 +268,15 @@ class TransReturn(db.Model):
operator = db.Column(db.String(100))
return_time = db.Column(db.DateTime, default=beijing_time)
# ★ 公司快照:退回发生时的所属公司。
# 不靠 join 推 —— 隔离判定的链路是
# trans_return → trans_outbound → (source_table, stock_id) → 库存表 → 物料主表
# 而库存行会被入库模块**物理删除**(实测 1077 条出库记录中已有 7 条悬空),
# 链路一断,记录就会对普通用户静默消失。审计视图静默丢数据不可接受。
# 与 trans_defective_goods.company_name 同一处理方式。
# DDL 见 db_migrations/add_return_view_support.sql
company_name = db.Column(db.String(255), index=True)
def to_dict(self):
return {
'id': self.id,
@ -280,6 +289,7 @@ class TransReturn(db.Model):
'reason': self.reason,
'operator': self.operator,
'return_time': self.return_time.strftime('%Y-%m-%d %H:%M:%S') if self.return_time else None,
'company_name': self.company_name,
}