|
|
d6234622e8
|
chore(db): 借库转交完整部署脚本(生产可执行)
把本轮 6 个迁移(phase4 / 4b / 4c / 4d / 4e / 4f)按依赖顺序合并为一份
可直接在生产执行的脚本,并在一个模拟「部署前状态」的临时库上完整验证。
内容
----
1. trans_borrow 补 4 列(borrower_id / current_holder_id /
current_holder_name / dispatch_operator)+ 2 索引
2. trans_borrow_transfer 建表(最终形态)+ 7 索引
3. trans_borrow_return 建表 + 3 索引
4. 回填 trans_borrow 身份锚点(仅唯一命中者,重名/无法映射留 NULL)
5. 回填遗留转交流水的 borrow_no 与状态
6. 回填 reject_seen_at(存量拒收标记为已告知)
7. 从 remark 拆出被拼接的 reject_reason
8.(注释掉)borrow_transfer 权限码 —— 已无代码引用,默认不建
+ 执行后核对段 + 回滚段
★ 验证中发现并修掉两个真实缺陷(都不是「看起来能跑」能暴露的)
1) 顺序缺陷:第 5 段原先把遗留流水**一律**标成 ACCEPTED,导致第 6/7 段
按 status='REJECTED' 找行时一条都匹配不到(旧结构表里 status 是刚加的
列、全是默认 PENDING)。真正的信号在备注的 '[拒绝原因]' 标记里,
现据此还原真实状态。
2) 孤儿流水:第 5 段按 borrow_id 关联,来源借用行已被删除的流水匹配不上,
会永久停在 PENDING —— 在接收人那里变成谁也处理不掉的幽灵待办。
已加兜底把「没有单号」的遗留行一律结掉。
★ 两处 ⚠ 警示已写入脚本:第 5/6/7 段设计为**新代码上线前执行一次**;
若在功能已投产后重跑,会把当时真实的待接收/待告知记录误标。
验证方式:建临时库复刻部署前结构(旧 trans_borrow + 旧结构转交流水表 +
重名/无法映射/已归还/孤儿等边界数据),执行脚本后核对:DDL 与索引齐全、
身份锚点按唯一性正确回填、遗留流水状态从备注还原、原因正确拆出、
孤儿流水被结掉、无报错;再执行第二遍确认幂等(结果完全一致)。
|
2026-09-17 10:49:34 +08:00 |
|
|
|
681607bd43
|
feat(borrow): 拒收原因独立成列,与转交备注彻底分离
背景
----
拒收原因此前是**拼进 remark** 的:
transfer.remark = f"{remark}\n[拒绝原因] {reason}"
前端拿到的是「3333\n[拒绝原因] 5555」这样一坨,时间线上两句挤在一起,
无法分辨哪句是发起备注、哪句是对方拒收的原因。
改动
----
· trans_borrow_transfer 新增 reject_reason text 列;
reject_transfer 改为写入该列,不再拼进 remark。
· 存量按 '[拒绝原因] ' 标记切分回填(实测仅 #22:
remark 3333 / reject_reason 5555)。
· 时间线事件带出 reject_reason,前端才能分行展示。
★ 为什么拆列而不是让前端解析字符串
1) 拼接格式是隐式契约:改分隔符或加前缀,前端解析就静默失效且难排查;
2) 用户完全可能在备注里自己打出 '[拒绝原因]' 字样,按标记切分必然误判 ——
已加测试用例锁定该场景;
3) 结构化字段才能参与查询与统计(如按拒收原因归类)。
存储层能表达的东西,不该靠字符串约定去还原。
★ 一个迁移期踩到的坑:btrim 默认只去空格、不去换行。
拼接留下的是 '3333\n',只写 btrim(x) 会残留换行;必须显式给出字符集
btrim(x, E' \t\r\n')。已修正脚本并对存量做了一次清理。
验证(7 项断言全通过)
备注不被污染、原因写独立列、无原因时为 None、
用户备注含同名标记也不误判、库存零副作用、数据零残留。
|
2026-09-17 10:46:31 +08:00 |
|
|
|
f24797ff1f
|
feat(borrow): 拒收须告知发起方(责任回到他手上,不能静默)
背景
----
双向握手补上了「接收人确认」,却只做了单向告知:接收人能看到待办,发起方却
对结果一无所知。**被拒绝时物品责任仍在发起方手上** —— 他若不主动查列表,
就会误以为已经交接出去,责任链出现静默断点。
(ACCEPTED 不需要告知:东西已经交出去了,发起方无需动作。)
改动
----
· trans_borrow_transfer 新增 reject_seen_at(NULL 且 REJECTED = 尚未告知)。
★ 为什么需要持久标记而不是前端去重:换台电脑、换个浏览器就会重新提醒;
而这条信息的分量(责任归属)值得一个持久标记。
★ 存量已拒绝的流水一律标记为已告知:它们产生于本功能上线之前,
追溯提醒只会打扰(实测仅 1 条:#22,验收时的测试数据)。
· get_unseen_rejects(user_id):返回「我发起、被拒、尚未告知我」的转交,
并批量解析物料名 —— 只说「某笔转交被拒」发起方仍不知是哪件东西还在
自己手上,必须让他一眼认出来。
· ack_rejects(user_id, ids):发起方确认后写 reject_seen_at,幂等。
· GET .../transfer/pending-count 的响应并入 rejects:与待接收数量共用同一次
轮询,前端不必多打一个请求。
· POST .../transfer/reject-ack:无 permission_required,同 accept/reject。
顺带补一处同源显示缺口
----
流转时间线里,被拒绝的转交与成功的长得一模一样 —— 发起方翻记录时同样会
误判。现将转交状态一并带出时间线事件。
验证(15 项断言全通过)
----
发起方收到待告知的拒绝(含物料名/接收人/拒绝原因);接收人与无关人看不到;
ack 后不再提醒且幂等;ACCEPTED 不产生告知;None/非法 user_id 均安全返回;
库存零副作用、数据零残留。
|
2026-09-17 10:44:00 +08:00 |
|
|
|
7d9cdeb297
|
feat(borrow): 转交粒度下沉到明细行,支持部分转交
背景(业务方推翻上一轮约束)
----
上一轮按「一张单同时只能有一个持有人」实现了**整单转交**,并把「单内出现多个
持有人」当作 bug 去修。业务方验收后明确纠正:
物理现场经常只转交部分工具(借了 2 件、只把 1 件转给别人),
单内多持有人才是符合现实的正常状态。
故转交粒度从 borrow_no 下沉回 trans_borrow.id(明细行)。
改动
----
· transfer_borrow:只操作传入的那**一行**明细,不再按单号整批覆盖。
转出方 = 该行当前持有人;数量 = 该行待还量。
· accept_transfer:只转移 transfer.borrow_id 指向的那一行 ——
整批改写会把别人手上的东西一并抢过来(部分转交下同单明细分属不同人)。
· 唯一性约束从「单号至多一条 PENDING」下沉为「明细行至多一条」:
同单的其他明细可以同时各自挂着待接收,互不阻塞 —— 这正是部分转交的语义。
· get_records 的 pending_transfer 改按 borrow_id 关联(原按 borrow_no),
否则同单多项待接收会互相覆盖。
· 删除已无用的 _load_slip_for_update。
★ 数量粒度:一行只支持**整行转交**。一行只能有一个 current_holder_id,
「同一行只转一部分」需要把这行拆成两行 —— 经业务确认,现场场景中
「借 2 件转 1 件」的两件本就是两条明细行,故该限制不影响实际使用;
接口对传入的非整行数量会明确提示「应另立一条明细行」。
数据层
----
无需改表结构:borrow_id 本就是流水的关联列,borrow_no 退化为单据归属与
分组展示用。仅补 (borrow_id, status) 复合索引支撑新的查询路径。
存量撕裂数据(BOR-20260917-0001 的「测试 / 杜邢宸」)按业务方选择**保留不动**
—— 它现在不再是 bug,而是部分转交的正常形态。
验证(合成 2 明细单,21 项断言全通过)
----
· 只转工具A:工具B 完全不受影响
· 同一张单可同时挂两条待接收,互不阻塞;同一明细重复发起被拒
· accept 工具A 后:A→测试,B 仍是杜邢宸(单内两个持有人)
· 两个持有人、以及待接收人,三方各自都能在列表中看到该单
· pending_transfer 挂在正确的明细行上,is_mine 判定正确
· reject 后主表持有人不变;非整行数量被拒并提示拆行
· 全程 available_quantity 无变化,库存精确还原、零残留数据
|
2026-09-17 10:12:31 +08:00 |
|
|
|
b271ca3a49
|
feat(borrow): 转交流水表补 borrow_no 与 status(双向握手数据层)
背景
----
1) **单内撕裂**:原 /transfer 收的是**明细行 ID**,只更新那一行。一张单有
2 条明细时,转交后一条归接收人、另一条仍是原借用人 —— 前端按 borrow_no
聚合便同时显示两个名字(实测 BOR-20260917-0001:108→测试 / 109→杜邢宸)。
2) **单向强塞**:原实现发起即生效,接收人在毫不知情的情况下背上资产责任。
本次改动
----
· 新增 borrow_no —— 单据身份。一次转交要覆盖该单**多行**,单行 ID 表达不了
覆盖范围;accept 时据此批量更新。borrow_id 保留为「发起时的代表明细」供追溯。
· 新增 status —— PENDING / ACCEPTED / REJECTED 状态机。
· 存量 1 行按旧语义(发起即生效)标记为 ACCEPTED 并回填 borrow_no:
它事实上已经生效,若标 PENDING,接收人会收到一条早已生效的待办。
· 建 borrow_no / status / (to_user_id,status) 索引,后者支撑「待我接收」查询。
同单号同一时刻只允许一条 PENDING(应用层强制),避免两个接收人争抢同一批实物。
|
2026-09-17 10:04:05 +08:00 |
|
|
|
b998b00889
|
feat(borrow): 借库转交一期数据层(迁移、转交/归还流水表、发货操作人列)
背景
----
原 trans_borrow 是单行记录模式,身份维度只有 borrower_name 一个字符串:
「谁借的」与「谁现在还拿着」是同一个字段,无法表达持有权变更;归还时
return_time/return_operator/return_signature 被逐次覆盖,部分归还下
「谁在什么时候还了多少」永久丢失。
本次改动(全部增量,无破坏性 DDL,可回滚)
----
1) trans_borrow 补列
· borrower_id / current_holder_id / current_holder_name —— 身份锚点
· dispatch_operator —— 执行借出的库管(operator_name 形参此前被接收却从未落库)
2) 新建 trans_borrow_transfer —— 转交流水,一行=一次转交,不做覆盖式更新
3) 新建 trans_borrow_return —— 归还流水,逐次记录,根治部分归还失忆症
4) 历史回填(已执行):85 行中 84 行 borrower_id 唯一命中 sys_user;
未归还的 32 行全部绑定 current_holder
5) 注册 borrow_transfer 权限码(无冒号形式,避免 _expand_operation_perms
前缀桥接把权限放大给所有持有 op_borrow:operation 的角色)
设计取舍
----
· current_holder 只回填「未归还」行:已归还=物品已回库、无人持有,保持 NULL。
这样「current_holder_id IS NOT NULL」本身就是「仍在某人手上」的有效信号,
归还校验不会对已结清单据误触发。
· 三张表都不建外键:与 trans_return / trans_defective_goods 一致 ——
库存行会被入库模块物理删除(实测已有悬空),台账必须能独立存活。
· dispatch_operator 不回填历史:执行人信息从未被采集,系统中不存在可回填的
数据源;用申请人或借用人冒充实物交接人比留空更危险。
验证:迁移已对 inventory_db 执行,核对段全部通过。
|
2026-09-17 09:17:51 +08:00 |
|
|
|
a252013573
|
feat(perm): 逆向物流专用权限码与菜单授权
为逆向物流的每个动作拆出独立权限码,终止此前复用其它模块权限的做法。
权限码一览(全部为无冒号形式,见下方说明):
outbound_return 出库退回
defective_list 不良品台账查看
defective_restock 不良品回库
defective_scrap 不良品报废
stock_change_status 库存状态变更
outbound_return_list 退回记录查看
另补建两个菜单:defective_goods(不良品在管台账)、outbound_returns(退回记录),
前者原先只有前端路由、sys_menu 中无对应行,导致页面在权限管理界面不可见、
其下权限也无从授予(sys_element.menu_code 有指向 sys_menu 的外键)。
默认授予:SUPER_ADMIN、SUPERVISOR、WAREHOUSE_MGR。
刻意不含 OUTBOUND —— 业务确认出库员只负责正向拣货发货,不参与逆向物流。
★ 为什么全部用无冒号形式,而不是 <menu>:<action>:
后端 _expand_operation_perms() 对带冒号的权限码做**前缀桥接** —— 只要用户
持有该菜单下任一以 :operation/:edit/:delete/... 结尾的权限就被放行。
实测验证过这个放大效应:
outbound_list:return <- 持有 outbound_list:operation -> True
outbound_return <- 持有 outbound_list:operation -> False
且前端 hasPermission() 是精确匹配,用冒号码会出现「接口能调、按钮却看不到」
的错位。无冒号码不触发桥接,前后端判定完全一致。
★ add_return_view_support.sql 另含一处 schema 变更:trans_return 补
company_name 快照列。退回流水的多租户隔离原先只能靠 join 链推
(trans_return → trans_outbound → 库存表 → 物料主表),而库存行会被入库
模块物理删除(实测 1077 条出库记录中已有 7 条悬空),链路一断该记录就会
对普通用户静默消失。审计视图静默丢数据不可接受,故落快照。
表当前为空,无需回填。
三个脚本均为幂等,含预检、回滚段与执行后核对。
|
2026-09-16 16:44:46 +08:00 |
|
|
|
69c38a1bf7
|
feat(return): 逆向物流数据模型与迁移
新增原单退回与不良品在管的持久化结构。
- TransOutbound 增 returned_quantity(numeric(19,4),非 float):该值参与
「return_qty <= quantity - returned_quantity」判等,浮点误差会让反复部分
退回后出现「已退满却判定未退满」的错判
- 新增 TransReturn:退回流水,每次退回写一条而非覆盖式更新。刻意与
trans_borrow 划清界限——后者部分归还时会覆盖 return_time/operator,
导致归还历史永久丢失
- 新增 TransDefectiveGoods:不良品在管台账。坏件全程不入库存表,因为
status 是行级属性而质量是件级属性,把坏件加回原行只能整行打不良
(实测 stock_buy 单行最大 4789 件、中位 8 件,整行打不良会凭空损失良品)
- 状态机:待处理 → 处理中 → {已回库|已报废|已闭环}。终态由累计去向推导
而非「最后一次动作」——一批坏件可能既回库过又报废过,按最后动作定状态
会产生误导
- restocked_qty/scrapped_qty 两列:二期用 quantity-remaining_qty 反推回库量,
三期加入报废出口后该反推失效
- 审计白名单与模型预加载同步登记(监听器绑定 18 → 20 个模型)
迁移脚本均为纯追加式 DDL,含预检、回滚段与执行后核对。首个脚本用
COALESCE 包裹数量列——库存表允许数量为 NULL,而「NULL 大于 0」求值为
NULL 而非真,裸写会让脏行在预览与诊断两次查询里凭空消失。
|
2026-09-16 15:45:22 +08:00 |
|
|
|
fbc9296056
|
feat(stock): status 硬隔离与出库通道封堵
激活库存表长期「只写不读」的 status 列,使其成为分配准入的硬门槛。
- inventory_reservation: 新增状态语义单一事实来源(STOCK_STATUS_*/
allocatable_filter/is_allocatable);verify_scanned 增加状态准入校验
(覆盖出库与借库两条提交路径)
- outbound: _allocate_bom_requirements 过滤条件加 allocatable_filter,
非「在库」一律不进入候选集;扫码路由把 ValueError 转为 400
- outbound_service: create_outbound_batch 强制 request_id 必填。原先无单
时会跳到所谓「散单」分支,而该分支的扣减逻辑从未落地(注释点名的
_apply_reservation_override 全仓不存在),实测只写台账不扣库存,
同一批货可被反复出库;get_stock_by_barcode 接入扫码准入;维修单扫码
补排除「报废转出」;预占再平衡块补 rollback 兜底
- fill_missing_stock_status.sql: 把存量行刷回「在库」。该过滤是
Fail-Closed 的,不先洗刷会让全库无法出库
破坏性变更:不传 request_id 的出库请求现在会被拒绝。
|
2026-09-16 15:45:02 +08:00 |
|
|
|
d1694bf245
|
perf(stocktake): 去掉库位树的全展开渲染,并为推荐查询补索引
业务反馈切到抽盘面板与点【获取推荐】时卡顿。实测定位:
【真正的原因在前端】先跑 EXPLAIN ANALYZE 排除了后端 ——
推荐查询在当前数据量下仅耗时 1.3ms,全走 Seq Scan + Hash Join,
Buffers shared hit=161 全部命中缓存。所以卡顿来自 DOM:
el-tree 原先带 default-expand-all,会把全库 3371 个库位节点一次性铺进 DOM。
去掉 default-expand-all 后初始只渲染根节点。勾选状态与 getCheckedNodes
不受影响 —— el-tree 的 Node store 会预先构建全树,展开与否只影响 DOM。
【索引仍补上,但说明白】实测 pg_indexes:stock_* 三表的 warehouse_location
已有索引,但 trans_outbound / trans_borrow 只有主键,时间字段与
(source_table, stock_id) JOIN 键都缺;三张库存表的时间字段也缺。
新增迁移 add_active_location_indexes.sql 补 7 个索引。
诚实说明写在迁移头部:当前数据量下加索引后本查询**仍会走 Seq Scan**,
这是小表下的正确计划,索引是为 90 天/半年后的数据增长做前瞻,不是修当前的慢。
【loading 状态】treeLoading / recLoading 已在既有实现中就位并闭环在
try/finally 中,分别绑在 el-tree 的 v-loading 与【获取推荐】按钮的 :loading 上,
本轮复核确认,未做无谓改名。
实测: 索引后执行计划仍为 Seq Scan,Execution Time 0.769ms(预期内)
|
2026-09-11 14:38:40 +08:00 |
|
|
|
aeb852c64d
|
feat(stocktake): 新增盘点会话表 StocktakeSession
盘点原本只有 stocktake_draft(草稿行),没有会话实体,导致三个问题:
1. 盲盘/明盘、全盘/抽盘这类**会话级配置**无处存放,塞进草稿表就要每行
冗余一份,改一次配置得 UPDATE 上万行;
2. 「空会话」无法表示 —— 会话开了但还没扫码时草稿表里没有行,旧实现只能
靠 max(scan_time) 猜哪个会话活跃,于是新开的空会话会被其他 PDA 忽略、
反而加入上一轮的旧会话,多端协同直接分裂;
3. 没有状态字段,generate-missing 跑完后旧会话仍被当成活跃。
本表把活跃会话判定变成 company_name + status='active' 的精确查询。
迁移的要点:
- 唯一部分索引 uq_stocktake_session_one_active 保证「一个公司同时只有一个
活跃会话」,从数据库层挡住多台 PDA 并发开启导致的进度分裂;
因此 /draft/start-new 必须先把原有活跃会话置为 finished 再插入新记录。
- CHECK 约束限定 mode/scope_type/status 的取值域。
- 不回填存量:旧草稿没有对应会话行,部署后显示「无进行中的盘点」,数据不丢。
执行: docker exec -i inventory_db psql -U test -d inventory_system < db_migrations/add_stocktake_session.sql
|
2026-09-11 13:28:43 +08:00 |
|
|
|
6586d3cb75
|
feat(stocktake): 盘点草稿表增加公司隔离字段
stocktake_draft 原本没有任何公司维度,导致开启新盘点清空整表时会误删
其他公司的进度,扫码时也无法区分不同公司的同码物料。
- StocktakeDraft 增加 company_name(带索引),to_dict 同步输出
- 新增迁移 add_stocktake_draft_company_name.sql:加列 + (company_name,
session_id) 复合索引;存量行保持 NULL(超管仍可见),不做删除
执行: docker exec -i inventory_db psql -U test -d inventory_system < db_migrations/add_stocktake_draft_company_name.sql
|
2026-09-11 11:33:04 +08:00 |
|
|
|
ec66c33b06
|
feat(scan-draft): 扫码草稿表与接口,支持暂停后继续扫码
场景
----
扫码出库/借库的作业可能很长(一张单几十项),工人常需中途暂停去处理
更紧急的单据。改造前切换单据会清空已扫内容,刷新/退出页面则全部丢失。
由于库存在申请审批通过时已**预占**,暂停期间货不会被他人抢走 ——
因此草稿只记录「扫到哪了」,**不涉及任何库存操作**。即使草稿丢失也只是
需要重扫,不会造成库存错乱。
隔离粒度
--------
按 (user_id, biz_type, request_id) 一人一单:每个人扫自己的草稿,互不影响;
同一人可同时持有多张单据的草稿(正是「暂停 A 去出 B」的场景)。
user_id 一律取自 JWT,不接受入参覆盖,故不可能读写他人草稿。
为什么整单存一个 JSON(而非每条明细一行)
------------------------------------------
1. 保存是「全量覆盖」语义,逐行存无增量更新的收益;
2. 恢复时需要物料名称/规格/库位等展示字段,逐行方案只能回查申请单的
items_json —— 而历史单据的 items_json 不含 stock_id,回查会错配。
整单快照把展示字段一并存下,恢复零依赖,对老单据同样可靠。
接口
----
GET /api/v1/scan-draft 读取草稿
POST /api/v1/scan-draft 保存(全量覆盖;空清单则删除)
DELETE /api/v1/scan-draft 清除(提交成功后调用)
GET /api/v1/scan-draft/overview 各单据进度,供下拉徽标
实现要点
--------
· items_json 列是 jsonb,模型必须用 db.JSON —— 用 db.Text 会让 psycopg2
拿到 Python list/dict 时无法适配,报 "can't adapt type 'dict'",而异常
被接口的 except 吞掉后 POST 仍返回"成功",问题极难发现(开发中实际踩到);
· 概览接口做防御:残留的空草稿不参与展示。
实测:保存/读回、跨单据隔离(B 读 A 的草稿为 0 项)、全量覆盖、
提交后清除,全部符合预期。
|
2026-09-10 17:21:14 +08:00 |
|
|
|
2045a89330
|
feat(db): 审批单据时间口径统一 + 补注册 scrap_selection 权限
unify_approval_timezone.sql:
· scrap_approval 的 4 个时间列由 timestamptz 改为 timestamp without time zone,
与 outbound/borrow 对齐(该表当时无业务数据,转换零损失);
· 三张审批表 created_at/updated_at 的 DB 默认值由 UTC 的 now()/CURRENT_TIMESTAMP
统一为 timezone('Asia/Shanghai', now()),杜绝绕过 ORM 的写入偏 8 小时。
add_scrap_selection_perm.sql:
GET /scrap/scan 一直声明需要 scrap_selection,但该权限码从未写入
sys_element / sys_role_permission,精确匹配与前缀展开匹配全部落空,
导致除 SUPER_ADMIN 外没有任何角色能调用扫码接口。现补注册,
并授予所有已持有 scrap_execute(按单报废执行)的角色。
|
2026-09-10 10:14:19 +08:00 |
|
|
|
3d8fb198c5
|
feat(scrap): 报废审批流 DB 迁移(建表 + 权限码)
- scrap_approval 表(申请单,items_json 携带 source_table+stock_id 精准实物)
- trans_scrap 增加 scrap_request_no 关联申请单
- 注册权限码 scrap_apply / scrap_approval / scrap_execute 并按现有报废角色授予
|
2026-09-10 09:46:52 +08:00 |
|
|
|
c8cf2bd52b
|
feat(db): BOM 三态互斥存量归一——归档(废弃)自动停用
- 将历史“归档且仍启用(True/True)”的行归一为 归档且停用(True/False)
|
2026-09-09 13:00:35 +08:00 |
|
|
|
4146f35010
|
feat(permission): “出库/借库需审批”开关权限化——仅主管/超管可见可改
- 新增权限码 material_list:isApprovalRequired,授予仅 SUPER_ADMIN/SUPERVISOR(收回其它角色)
- 物料列表该列仅持码角色可见;后端批量端点与字段脱敏均改按新码校验,其余角色看不到值也无法改
|
2026-09-09 10:47:36 +08:00 |
|
|
|
e03c035ee0
|
feat(db): material_base 增加 is_approval_required 开关列(出库/借库需审批)
- boolean NOT NULL DEFAULT false,存量默认不审批
|
2026-09-09 09:31:12 +08:00 |
|
|
|
6250fa94cb
|
feat(bom): BOM 子件引用版本与归档 数据库迁移
- bom_table/bom_draft_table 加 child_bom_no/child_bom_version(子件引用的自制BOM版本)
- bom_table 加 is_archived 归档标记(仍启用可查看/编辑,但不再作为子件版本候选)
- 回填 60 行自制件子件引用版本,4 个多版本自制件按业务口径(79/92/226/1905)
- 3 套占位/废弃配方改标归档并恢复启用
|
2026-09-08 10:14:40 +08:00 |
|
|
|
9c3fcbca4c
|
migration: 补齐 sys_element 缺失的 :operation 权限码 + 角色分配 SQL
|
2026-07-17 15:29:59 +08:00 |
|
|
|
3c0954598c
|
perf: 综合安全加固 — RBAC严格映射+异步邮件+字段权限白名单+前端对齐+导入模板
本次提交包含本会话所有修改的最终统一提交
## 权限系统重构
- permission_service.py: 添加入库/采购操作元素 + ensure_default_permissions
- field_permissions.py: 严格1-to-1 Default Deny 字段映射(StockBuy/Semi/Product/MaterialBase)
- decorators.py: _expand_operation_perms 双向粒度桥接 + prevent_double_submit
- deploy_production.sql: 修复 sys_element 别名码(qty_inbound→in_quantity)
## 采购模块
- purchase.py: 权限驱动可见性 + inbound_purchase独立权限 + 价格字段过滤
- purchase_service.py: 异步邮件 + 三阶段批量模糊匹配防N+1
- purchase/index.vue: canApprove严格操作权限 + upload重复修复
## 导出/入
- base_service.py: export_excel 流式写入防OOM + get_latest_specs 优化
- import_service.py + import_api.py: Excel批量导入(模板+预览+执行)
- ImportDialog.vue: 三步骤导入弹窗
## 异步邮件
- email_service.py: send_email_async (守护线程)
- inventory_task.py: send_email→send_email_async
## 前端对齐
- product/semi/buy.vue: 列对齐in_quantity/stock_quantity/available_quantity + localStorage缓存V2
- buyOdoo.vue: 排序修复 + 导入按钮 + 移除点击展开加载
- BomManage.vue: 懒加载分组 + 导入按钮
- list.vue: 导入按钮
- Selection.vue + borrow/apply: BOM匹配修复 + 导入按钮
- outbound/create.vue: 出库类型必选
- AppMain.vue: 移除transition白屏修复
- material_base.ts, outbound.ts, bom.ts, stock.ts: 新增API函数
|
2026-07-17 13:07:12 +08:00 |
|
|
|
4934cd4d8f
|
feat: 采购管理税率+分开单价总价列 + 按单出/借库锁定扫描 + 含税法计算
- purchase: 新增tax_rate字段(model+service+frontend), 表格拆分为不含税单价/总价/税率三列
- buy.vue: 含税法计算(含税总价=数量×含税单价), 含税总价自动计算可手动覆盖
- create.vue: 按单出库模式下未选审批单时锁定摄像头和SKU输入
- borrow.vue: 未选审批单时锁定摄像头和SKU输入
- deploy_production.sql: 整合全部数据库变更(表结构+权限码+角色分配)
|
2026-07-15 16:07:59 +08:00 |
|
|
|
4b1a86a870
|
fix: 对齐field_to_perm与sys_element权限码 + 税率6% + 含税总价反算
- buy/semi/product: 数量字段映射对齐sys_element实际注册码(qty_inbound/qty_stock/qty_available)
- 数据库: 补全buy(9)+semi(10)+product(13)缺失权限码
- deploy_production.sql: 同步更新
- buy.vue: 税率新增6%选项, 价格联动以含税为主输入, 税率变化保持最后编辑价格不变
- buy.vue: 新增含税总价可输入反算功能
|
2026-07-15 15:03:37 +08:00 |
|