Commit Graph

108 Commits

Author SHA1 Message Date
511e822d12 feat(ui): 按钮级权限守卫与数量显示格式化
按钮级权限(与后端 @permission_required 用同一批权限码):
- 出库记录页「退回」按钮   -> v-permission="'outbound_return'"
- 不良品看板「修复回库」   -> v-permission="'defective_restock'"
- 不良品看板「报废销毁」   -> v-permission="'defective_scrap'"

  两个处置按钮是**独立权限**,可能有人只有其一,故分别判定、分别隐藏;
  两者都无时显示「无处置权限」。均保留响应式的 v-if 兜底——v-permission
  只在 mounted 执行一次,而 Element Plus 表格行会重渲染,两者用同一权限码
  判定一致。hasPermission() 内部已对 SUPER_ADMIN 放行,与后端装饰器的
  超管旁路对齐。

  另外去掉了出库记录页原本套用的 username === 'IRIS' 特例放行:退回是实物
  交接的 SOP 动作,后端不认这个特例,前端也不该认,否则会出现「按钮可见但
  接口 403」的错位。

数量显示格式化(新增 utils/format.ts,该文件原本是 0 字节空文件):
- formatQty()    显示用:1.0000 -> '1',2.5000 -> '2.5'
- normalizeQty() 数值用:同上但返回 number,供 el-input-number 的
                 v-model / :max —— 喂字符串会让步进与边界比较退化

  应用于 不良品看板(退回总数、剩余待处理、两个弹窗的在管数量与输入框)、
  出库记录页(已退 N 提示、退回弹窗三项数字)、退回记录页(退回数量列)。

  同时移除三个 el-input-number 上的 :precision="4" —— 它会把 1 强制渲染成
  1.0000,是截图上多余小数点的直接来源。数量本身仍支持小数(后端按
  numeric(19,4) 收),只是不再强制补零。

  表格列原本不受影响(接口返回 JSON 数字,1.0 经 JS 解析即 1),但后端有一批
  运算是 float 累加,可能吐出 0.30000000000000004 这类尾巴,格式化按
  numeric(19,4) 量纲先收敛到 4 位再做归一。
2026-09-16 16:46:20 +08:00
27436f5efe feat(ui): 菜单权限过滤与退回记录页
侧边栏支持按权限隐藏菜单(Sidebar/index.vue):
- 新增 canSee():读取 meta.permissions(数组,满足任一即可)
- ★ 同时修了一个既有缺陷:原实现只过滤顶层路由,**子路由的权限判定不生效**,
  未授权用户仍能在菜单里看到入口。现改为递归过滤,父菜单的子项被过滤光后
  父菜单本身也不再显示。
- ★ 刻意只认 meta.permissions(新键),历史遗留的 meta.permission(单数,
  如「维修管理」的 inbound_repair)保持不生效——一旦开始消费它,会让若干
  菜单对部分角色突然消失,属于超出本次范围的静默行为变更。两者若要统一,
  建议单独排期逐个核对影响面。

路由守卫(router/index.ts):
- 新增 meta.permissions 校验,未授权时提示并跳首页。侧边栏负责「菜单看不见」,
  守卫负责「直接敲 URL 也进不去」,后端负责「绕过前端直接调接口也不行」,
  三层任一失效都不至于越权。
- 顺带修了原守卫的分支结构:roles 通过时直接 next(),会跳过后续检查;
  改为不通过才中断,落到末尾统一 next()。

新增「退回记录」页(views/outbound/returns/index.vue):
- 纯只读台账,无任何操作按钮,供库管核对历史退回
- 展示 退回时间 / 原出库单号 / 物料名称 / 规格 / SKU / 退回数量 /
  退回类型 / 退回原因 / 操作人
- 退回类型用标签区分(不良品=红,良品=绿)
- 源库存行或原单被删除时显示占位文案而非空白,台账不留哑行

api/inbound/return.ts 补 getReturnList()。
2026-09-16 16:46:02 +08:00
fd99d33a0d feat(return): 前端退回入口与不良品在管台账看板
出库记录页(views/outbound/index.vue):
- 明细行新增「退回」列,returnable_quantity <= 0 时按钮置灰,行内显示「已退 N」
- 对话框展示 原出库数量 / 已退回数量 / 本次可退最大,默认带入可退最大值
- 退回类型用下拉单选:良品(加回库存)/ 不良品(转入异常待处理)
- 退回原因必填,提交前三重校验;按钮 :loading + 函数内 submitting 双保险防抖
- 成功后刷新列表。错误提示不重复弹——request 拦截器对 HTTP 400 已取
  data.msg 展示,对话框 catch 只收尾

配套后端(services/outbound_service.py):
- get_grouped_list 的出库明细补 id / returned_quantity / returnable_quantity。
  原先明细不带 id,退回接口无从指定 outbound_id

新增页面(views/stock/defective/index.vue,路由 /inventory/defective):
- 展示 物料名称/规格/SKU/退回时间/操作人/退回总数/剩余待处理/状态
- 顶部 alert 提示在管总量,并明确「这批实物不在库存表中,盘点请以本台账
  为准」——在管坏件对盘点不可见是本方案的固有盲区,必须在页面上主动提醒
- 仅对 remaining_qty > 0 的行提供「修复回库」「报废销毁」,终态行显示已结案
- 两个弹窗均带数量上限约束与 loading 防抖,成功后刷新

新增 api/inbound/return.ts 承载四个逆向物流接口。

侧边栏由 router/index.ts 驱动,加路由即入菜单;注意侧边栏只过滤
meta.hidden、不看 meta.permission,故菜单对所有角色可见,访问控制由后端
接口负责(与既有「维修管理」一致)。
2026-09-16 15:45:40 +08:00
fe54d01391 fix(outbound): 序列号物料出库被误判超出计划数量
现象:申请单里同一个物料有 2 个,扫第 2 个时报
「超出计划数量(计划: 1,已扫: 1,本次: 1)」。

根因:计划侧与已扫侧用了**不同的聚合方式**。
  已扫侧 .filter().reduce()  → 把【所有】匹配行加总
  计划侧 .find()             → 只取【第一条】匹配行
序列号物料在申请单里是「一物一行、每行 quantity=1」(因为每个序列号对应
一条独立库存行),于是 planQty 只拿到 1,而 alreadyScanned 是全部之和,
1 + 1 > 1 直接判超发。普通数量制物料计划单只有一行,两边一致,所以一直
没暴露 —— 只有序列号物料会踩中。

同一缺陷还有第二处:unscannedList 逐行比较 planQty 与「累计已扫」,
导致两行各自都被同一笔扫码"满足",扫 1 个就把整个物料判为扫完、未扫清单漏报。

修复:
- validateAgainstPlan:计划侧改为 .filter().reduce() 汇总所有匹配行
- unscannedList:先按「名称+规格」合并计划行,再与同口径汇总的已扫数比较

用申请单 410(Opt1025 两行各 1 个)的真实数据模拟验证:
  修复前 扫第2个 → 超出计划数量(计划:1,已扫:1,本次:1)  ← 与现场报错逐字一致
  修复后 扫第2个 → 通过
  修复后 扫第3个 → 仍被拦(计划:2,已扫:2,本次:1),超发保护未失效
2026-09-15 13:21:36 +08:00
b313fefdfa fix(web): 扫码页与备选库位带单据ID,按有效可用量限流
配合后端预占回加:扫码/借库/审批页面调用 /outbound/scan 与 /outbound/alternatives
时带上本单 id 与 biz_type,拿到的 available_quantity 即为「实时可用量 + 本单
预占」,本单锁定的行不会再因实时可用量为 0 而被拦住或从列表消失。

后端已在响应边界完成归一化,故前端模板与校验逻辑(:max、库存列展示、超量
警告、草稿存取)一律不动,只多传参数 —— 避免把同一语义散落到多个读写点。

- api/outbound.ts:getStockByBarcode / getStockAlternatives 加 requestId 与
  bizType 参数,导出 ScanBizType,ScanResult 补 reserved_quantity 等字段
- api/transaction.ts:同名的历史副本同步签名(当前无调用方,加注释指向唯一来源)
- views/outbound/create.vue、views/transaction/borrow.vue:各 3 处调用点带上
  单据 id;refreshStockFromDraft 改为显式透传 id,不依赖 computed 已解析
- views/borrow/approval/index.vue:审批页也带上本单 id,否则待审批单
  (预占已生效)锁定的行会因实时可用量为 0 而从备选列表消失

借库侧 request_id 是 BorrowApproval.id,靠 biz_type='borrow' 与出库单区分 ——
两张表 ID 空间独立,不可省。
2026-09-11 10:34:23 +08:00
ce767e39ac feat(scan): 扫码时检测单据失效,避免白扫一场
场景
----
库管正在扫码,这张单被撤回/驳回/执行了。若不检测,库管会一直扫到
提交时才发现单据已作废(后端会拒绝),前面的工作白做。

实现
----
新增 checkRequestStillValid():重新拉一次「已通过(status=1)」列表,
看当前单据是否还在其中。不在 → 已被撤回/驳回/执行,立即弹窗告知并
清空界面(草稿后端也已同步清除)。复用现有列表接口,无需新增端点。

触发时机(覆盖两类场景):
  · 页面重新可见时(visibilitychange)—— PDA 熄屏唤醒、切回标签页
  · 每 60 秒一次 —— 库管一直在页面上扫、没切走

三点取舍:
  · 仅购物车非空时检测(正在作业才打扰),空清单不发请求;
  · 60 秒轮询而非实时推送 —— 引入 WebSocket 长连接成本过高,
    此处最多浪费 1 分钟扫码,相比"扫完几十分钟才发现"已足够;
  · 检测失败不阻断作业(仅 console.warn)—— 后端提交时仍会做最终校验,
    前端检测是为了早发现,不是安全防线。

出库 create.vue 与借库 borrow.vue 两页同款实现。
2026-09-11 09:06:51 +08:00
a4a9afb6db feat(scan): 出库/借库扫码页接入草稿,切换单据不清空
交互
----
无需「暂停」按钮 —— 在下拉框切换单据这个动作本身就是暂停:
  切走 → 自动存当前单据的进度
  切回 → 自动恢复,并提示「已恢复上次的扫码进度(N 项)」
下拉框对扫到一半的单据显示橙色「已扫 N」徽标,不必逐个点开试。
提交成功后自动清除该单据的草稿。

修复的三个 bug
--------------
1) 切换时把 A 的内容存到了 B 名下
   v-model="selectedRequestId" 的 computed setter 会**先于** @change 把
   selectedRequest 改成新单,故 handleRequestChange 里读到的是新单。
   新增 activeRequestId ref 记录「界面上真正显示的是哪张单」,
   保存时显式传入离开的那张单的 ID。

2) 切回时把目标单的旧草稿删了
   原先写了「购物车为空则清除草稿」,但切换瞬间购物车必然为空,
   于是切回 A 时触发了清除。现改为空清单只跳过保存、不清除;
   清理由「提交成功」或「用户点清空列表」显式触发。

3) 恢复后名称/规格为空、出库数显示 NaN
   draftPayload 只存了 4 个字段(stock_id/source_table/sku/quantity),
   而购物车表格绑定的是 name/spec_model/available_quantity/out_quantity
   —— 全都没存。现保存完整快照,并在恢复时归一化
   (out_quantity ?? quantity)以兼容已存在的旧草稿。

补充:恢复后刷新实时库存
------------------------
草稿里的 available_quantity 是扫描那一刻的快照,跨时间恢复可能已过期
(期间别人出库/借出会消耗可用量)。恢复后复用 /alternatives 端点拉一次
实时可用量:数量超了会明确提示「N 项物料的实际库存已少于你扫的数量」,
避免工人扫满后到提交时才被后端拒绝。失败不阻断,沿用草稿快照。

另:离开页面(路由跳转)时存草稿并弹确认;beforeunload 用 sendBeacon
尽力保存(该路径无法带 Authorization 头,可能失败,但防抖保存已覆盖
绝大部分内容)。
2026-09-10 17:21:24 +08:00
ea52c78f9e feat(outbound): 计划出库清单数量前置,移除类型列
问题
----
计划出库清单的「计划数量」列排在表格最右侧(名称/规格/库位之后)。
表格共 6 列且名称规格会撑宽,最右列容易被容器裁掉,工人反馈"看不见数量"。

改动
----
· 「计划数量」从最右移到「序号」之后,橙色加粗 + 字号放大到 15px;
· 移除「类型」列(material_type);
· 库位列宽 150 → 170。

注:该列一直存在且数据正常(items_json 的 quantity 字段),
此前是可见性问题而非数据缺失。
2026-09-10 15:57:20 +08:00
dc4a0ae7a0 style: 统一申请单状态配色(四个页面一致)
配色方案
--------
  0 待审批 → 蓝(primary,待处理)
  1 已通过 → 绿(success,正向结果)
  2 已驳回 → 红(danger)
  3 已完成 → 灰(info,终态、不再需要关注)
  4 已撤回 → 黄(warning,需留意但非错误)

范围
----
「我的申请单」新页面按上述配色实现后,发现出库/借库/报废三个审批页
仍是旧配色(0:warning, 3:info, 4:info)—— 同一套状态码在两个页面
显示不同颜色,用户来回切换会困惑。故一并统一。

改动:my-requests/index.vue(新配色)+ 三个审批页的 statusTagType。

补充:el-tag 的 primary 类型已核实可用 —— Element Plus 2.13.1 的
tagProps 中 primary 为默认值,theme-chalk/el-tag.css 含 .el-tag--primary
的蓝色样式定义;vue-tsc 类型检查通过。
2026-09-10 15:47:07 +08:00
20d49c0b0d feat(my-requests): 我的申请单独立模块(采购管理之后)
路由位置
--------
放在「采购管理」之后,作为独立一级模块:

    采购管理
    我的申请单      ← 新增(icon: Tickets)
    借库管理

★ 独立模块而非挂在某个业务页下:申请人可能提交出库/借库/报废三类单据,
  放在任一业务模块下都会让其它模块的人找不到。

页面功能
--------
  · 类型筛选:全部 / 出库 / 借库 / 报废
  · 状态筛选:全部 / 待审批 / 已通过 / 已驳回 / 已完成 / 已撤回
  · 展开行显示物料明细(数量字段已由后端归一化)
  · 撤回按钮仅对 status 0/1 可见(已预占但未执行)

撤回不硬编码 URL —— 使用后端回传的 withdraw_endpoint 分发:

    const res = await request({ url: row.withdraw_endpoint, method: 'post' })

这样将来模块端点调整(如借库从 close 换成正式 withdraw),前端无需改动。

权限说明
--------
菜单不做权限过滤(Sidebar 只过滤 meta.hidden),所有角色都会看到本页。
这正是预期 —— 申请人本就不应需要任何权限码。数据由后端强制按
applicant_id 过滤,看不到他人单据。

顺带清理
--------
移除先前加在 Selection.vue 的重复实现(约 110 行模板/脚本/样式),
改为一个跳转链接「查看我的申请单 >」;并移除随之失效的 3 个导入
(onMounted / Refresh / 两个 API 函数)。

验证:新页面 SFC 编译零告警;Selection.vue 清理后编译通过。
2026-09-10 15:37:11 +08:00
d1dd3dd404 feat(approval): 三端审批页「撤回」入口,明确告知会释放库存
后端已支持撤回时释放预占(见上一提交),前端同步:

一、文案与语义
  按钮 完结 → 撤回,颜色 danger → warning(语义从「危险操作」变为
  「可逆的库存释放」)。确认弹窗明确告知会释放多少项库存:

    确定撤回申请单【APR-OUT-...】吗?
    撤回后该单将被作废,其预占的 5 项物料库存会立即释放,
    可供其它申请使用。此操作不可恢复。

  用户看到的「1 件货」背后其实是一批被锁定的库存,不写清楚会让人
  以为撤回只是「关掉一张单」。

二、报废新增撤回入口
  报废审批页原先只有「执行报废」,没有撤回。补上按钮 + handleWithdraw(),
  并新增 API 封装 withdrawScrapRequest()。
  状态映射同步补充 4: '已撤回'(statusText / statusTagType)。

三、成功提示改为透传后端消息
  后端会返回「申请单已撤回,释放 N 项预占库存」,比前端写死的文案
  更有信息量,故改为 res?.msg 优先、前端文案兜底。

按钮可见性沿用 row.status === 1(已通过但未执行),与后端的执行守卫
(执行成功后 status 置 3)一致,故已执行或已撤回的单不会出现该按钮。
2026-09-10 15:08:43 +08:00
209b29c10f feat(outbound): 备选库位可见性,让「物理覆盖」不再盲扫
问题
----
预占会把货锁定在某个库位,但工人到现场可能进不去/找不到该库位,
需要改扫同物料的其它批次。后端执行端已支持按 base_id 校验、允许换批次,
但系统从不告诉他「还有哪些库位有货」—— 工人只能凭记忆或挨个翻。

后端:新增 GET /api/v1/outbound/alternatives
--------------------------------------------
  入参 base_id(必填)、source_table/stock_id(可选,用于标注推荐行)
  返回该物料全部可用库存行 + 合计可用量,推荐行置顶、其余按可用量降序。

为什么不复用 stock/list 或 bom-match-stock 的查询模式:
那两处按 stock_quantity > 0 过滤,会把「有货但已被别单全部预占」的库位
也列出来,工人跑过去才发现拿不到。实测库中有 14 行处于该状态。
本接口按 available_quantity > 0 过滤,只给真正能拿的库位。

前端:计划清单库位列加图标 + popover
------------------------------------
  [推荐] Y1/2/1  可用 5      ← 本单锁定行(来自 items_json 的 stock_id)
  [备选] Y2/3/4  可用 10
  [备选] Z1/1/1  可用 2

三处取舍:
  · trigger="click" 而非 hover —— 车间用扫码枪/触摸屏,hover 在触屏不可用
  · @show 时才发请求 —— 计划清单可能几十行,渲染即请求会打出一片并发
  · 附提示文案「现场取不到推荐库位时可直接扫备选库位条码出库」

注:历史单据的 items_json 无 stock_id,此时所有库位显示为「备选」
(不影响可用性,仅少了推荐标记);预占改造后新提交的单可正确标注。

实测:造 3 批次 Y1/2/1(5) Y2/3/4(10) Z1/1/1(2),预占首个后其 available=0,
接口正确排除该库位,返回两个备选、合计可用 12。
2026-09-10 14:59:17 +08:00
0a70e5688a refactor(bom): 前端改为消费后端分配结果,删除本地分配与跨页数据污染
一、删除前端分配算法
  Selection.vue 与 borrow/apply/index.vue 原先各自维护一套
  rowsByBaseId 归并 + 跨行分配循环(近 80 行),现全部移除。
  改为:构造 requirements → 调用 bomMatchStock → 直接 push 返回的 items。

二、缺料提示
  后端返回 shortages 时,用 ElMessageBox.alert 逐项列出
  「物料名:需 X,实配 Y,缺 Z」,替代原先笼统的「跳过 N 种缺货物料」。
  全部满足则不弹窗,仅提示添加成功。

三、API 封装支持双签名(api/outbound.ts)
  bomMatchStock(requirements | { requirements })
  同时兼容旧的 bomMatchStock(childIds),未改造的调用方不受影响。

四、清除 loadStockForBom(两个页面)
  该方法会把 BOM 匹配结果整体写入 stockList.value,而 stockList 同时是
  「手动选单弹窗」的数据源 —— 操作过 BOM 后再打开手动选单,看到的会是
  BOM 匹配结果而非库存列表。BOM 流程不再触碰 stockList 后,该交叉污染
  一并消除,方法随之删除。

注:借库选单页存在逐字相同的 .find() + Math.min 缺陷,本次一并修复,
使两个 BOM 入口行为一致。

SFC 编译通过;vue-tsc 无新增错误(残留 6 处告警为既有问题)。
2026-09-10 14:16:52 +08:00
e437b3cece feat(records): 高级筛选引擎 + 出库记录接入
一、新增共享工具 app/utils/advanced_filter.py
  系统内已有该模式(material/list.vue、stock/inbound/buy.vue),
  沿用其既有约定:参数名 advancedFilters、值为 JSON 字符串、
  操作符 eq/ne/contains/not_contains/ge/le。

  · parse_advanced_filters() 解析并规整,坏输入退化为空列表不影响主查询
  · build_predicate() 单条件 → SQLAlchemy 谓词,未登记字段返回 None 杜绝列注入
  · build_material_name_select() 物料名三表联查(buy/semi/product JOIN material_base)

二、★ 父子关系处理(本次核心)
  记录接口返回的是**按单号分组的订单**,而用户筛选字段多落在**明细行**上。
  若直接 .filter(TransOutbound.sku.ilike(...)),会在 GROUP BY 前收窄明细范围,
  展开行里的兄弟明细会凭空消失。正确做法是先求「含匹配明细的单号集合」
  再让主查询按单号 IN 过滤。

  实测对照(单 OUT-20260811-1519-0003,21 条明细):
    按其中一条 SKU 筛选 → 子查询法保住全部 21 条;直接 filter 只剩 1 条。

三、★ 否定操作符语义(NOT IN)
  子级字段的 ne / not_contains 不能直接用 SQL != / NOT LIKE —— 那表达的是
  「本单存在某条不等于 X 的明细」,多明细单几乎必然成立,等于筛选失效。
  用户意图是**整单排除**,故 apply_child_condition() 统一:
    肯定 → order_no     IN (含匹配明细的单号)
    否定 → order_no NOT IN (含匹配明细的单号)
  两者子查询完全一致(都用肯定形式谓词),仅外层取反。
  父级字段(单号/操作人)仍走标准 SQL 谓词,语义无歧义。

四、出库记录接入(前端弹窗 + 后端接线)

验证:
  eq 0000000002 → 1 单;material_name contains 白板 → 16 单
  sku ne 0000000002 → 394 = 395-1,含该 SKU 的单被整体排除
  material_name not_contains 白板 → 379 = 395-16
2026-09-10 13:05:59 +08:00
c35ec9a659 feat(records): 出库/借还记录统一高级搜索版式
三个记录页(出库/借还/报废)此前搜索区形态各异:出库用裸 div + 分散样式,
借还缺日期范围,报废只有 SKU 输入框。现统一为 el-form :inline="true" +
.filter-form 版式,并补齐缺失的过滤维度。

借还记录(新增日期范围能力):
  · 后端 get_records 增加 start_date/end_date 参数,按「借出时间」过滤;
  · 边界补全时分秒(YYYY-MM-DD → 当日 00:00:00 / 23:59:59),
    与出库记录同口径,解决零点截断导致当天记录漏查的问题;
  · API 层透传两个新参数。

出库记录:
  · 版式统一为 inline 表单,日期选择器加 label;
  · 新增「重置」按钮;
  · 搜索类型切换由 500ms 防抖改为立即查询。

借还记录:
  · 新增「重置」按钮;搜索类型/状态切换改为立即查询(取消防抖)。

实测(SUPER_ADMIN):
  报废 全部/单号/SKU/操作人/物料名/日期 六类过滤均 200 且命中正确;
  借还 无过滤 52 单 / 本月 18 单 / 空区间 0 单(日期过滤生效);
  出库 无过滤 395 单 / SKU 277 / 姓名 7 / 物料名 16 / 单号 OUT-2026 命中 395。
2026-09-10 12:06:08 +08:00
a12fcea86d fix(borrow,outbound): 库管弹窗必显审批人 + HTTP400去重toast
- 审批人 el-form-item 显隐加入 role==WAREHOUSE_MGR 判断
- 提交 catch 仅对无响应(网络)弹错,HTTP错误由全局拦截器按后端 data.msg 弹一次,避免重复
2026-09-09 16:19:45 +08:00
6bdc8a82f2 feat(borrow,outbound): 申请预检按需显示审批人——默认不显示,命中需审批才出现并必选
- 后端新增 /borrow/request/check-approval 与 /outbound/request/check-approval 预检接口
- 申请弹窗默认隐藏审批人;提交前预检:命中需审批→显示审批人并红字列物料、未选拦截;未命中→隐藏并直接提交(approver=null)
- 申请 items 补传 base_id 供后端 ID 精确判定
2026-09-09 10:47:29 +08:00
2abac8b3f9 feat(borrow,outbound): 申请页审批人改可选(默认免审批),提示语更新
- 出库/借库申请不再强制选审批人;命中需审批且未选人时由后端提示补选
- 成功提示改为:含需审批物料将进入审批,否则直接交库管执行
2026-09-09 09:31:28 +08:00
9fc6ff00f9 feat(bom): 出库/借库清单头部 BOM 号后显示父件物料名
- selectedBomLabel 追加父件名(优先候选列表 parent_name,回退最近详情 parent_name)
- 主清单与欠料待补清单两处头部同时生效;取不到名称时退回原格式
2026-09-08 11:13:42 +08:00
7d9828e403 feat: BOM套餐添加弹窗齐套清单显示自制件子件版本号 2026-09-07 18:03:43 +08:00
3dd8458de0 feat: BOM 子件展示自制件 BOM 版本号——子件列表与出库/借库 BOM 清单打印均显示 2026-09-07 18:01:16 +08:00
803e8a1532 fix: 出库申请的出库类型改为不默认、必填校验(不能为空提交) 2026-09-07 14:04:34 +08:00
478b90fe50 feat: 出库类型贯穿申请→扫码出库——申请单加 outbound_type,申请时选类型、扫码出库自动带出 2026-09-07 14:01:13 +08:00
eaafeaf24e feat: 出库类型新增「维修出库(REPAIR)」下拉选项与记录类型/标签映射 2026-09-07 13:51:38 +08:00
1527d552d4 feat: 出库/借库记录记录并展示库位快照(DB加列+后端写入+记录页展示) 2026-09-04 11:42:44 +08:00
129cb1119f fix: 出库选单 BOM 下拉唯一值修复,打印模板补齐欠料待补区与借库对齐 2026-09-02 18:39:17 +08:00
c3637e0c25 fix: 出库备注欠料改用实时库存计算,不再自动填历史欠料
- buildRemarkWithShortage 改用 bomDetailList 实时 shortage(需量-当前库存)
- 库存补足后,欠料不再自动拼进出库备注(原 localStorage 历史记录一直带出)
2026-09-01 16:35:32 +08:00
5bd6fc6272 fix(web): 出库记录类型映射补充 LOSS(盘亏出库) 中文显示
扫码出库下拉存在「盘亏出库 LOSS」选项,历史记录中已存在 LOSS 类型数据,
但出库记录页 formatType/getTagType 翻译表缺失 LOSS 映射,导致记录原样显示英文 LOSS。
补充映射后 LOSS 显示为中文「盘亏出库」黄色标签,与其他出库类型口径一致。
2026-08-31 16:12:18 +08:00
f5aa2f481b feat(scan): 出库/借库扫码增加未扫清单弹窗
- 扫码进度条旁新增「未扫清单 (N)」按钮(有未扫满物料时显示)
- 弹窗列出计划中未扫满的物料:名称/规格/计划数量/已扫数量/待扫数量
- 提供「去扫码」快捷跳转,方便补扫漏掉的物料
- 未扫满 = 计划数量 > 已扫数量(含完全未扫和扫了一部分的情况)
2026-08-31 14:53:20 +08:00
e026c00a85 refactor(outbound): 移除扫码出库页的强制完结按钮
- 删除 create.vue 中「强制完结此单」按钮及对应逻辑
- 完结功能保留在出库审批列表页(approval/index.vue)
- 出库执行页聚焦扫码作业
2026-08-31 14:38:51 +08:00
e492000c3e feat(scan): 出库/借库扫码进度显示 + 重复扫码确认
- 出库 create.vue + 借库 borrow.vue 均增加扫码进度条
  显示: 已扫/总数 种 + 已扫/总数 件 + 进度百分比
- 重复扫码时不再直接 +1,改为弹窗确认「确认 +1 / 取消」
  防止手滑重复扫或误操作
- 扫码进度条选中审批单后显示
2026-08-31 14:35:26 +08:00
5abc504619 feat(outbound): 审批列表页增加完结按钮 + 优化缺货提示文案
- 去掉缺货提醒末尾的『是否查看?』(纯文案,保持强提醒但不承诺展示清单)
- approval/index.vue 对已通过(status=1)的审批单新增「完结」按钮
  库管/主管/超管可见,完结后从已通过列表移除
- 筛选区新增「已完结」状态选项,状态标签支持 status=4
2026-08-31 10:46:33 +08:00
39f3f8af45 feat(outbound): 审批单手动完结/作废功能
- 后端: outbound_service 新增 close_request 方法(状态 1-已通过 → 4-已完结)
- 后端: 新增 POST /api/v1/outbound/request/<id>/close 接口
- 模型: status_text 数组增加「已完结」
- 前端: create.vue 审批单选择区新增「强制完结此单」按钮
  完结后刷新列表,该单从已通过下拉中移除
- 权限: 超级管理员/审批人/拥有 outbound_create:operation 的用户可完结(库管可操作)
2026-08-31 10:15:33 +08:00
b6ec45a8b8 feat(outbound): 出库流程强化(缺料防漏 + 完全移除直接出库)
① BOM 清单 SKU 改为规格型号(getBomWithStock 返回 children 无 sku,只有 child_spec)
② 缺货清单自动写入出库申请备注(借鸡生蛋:历史欠料永久留存在申请单)
③ 打印单底部增加欠料待补签收区(补料人签字/日期)
④ 有缺料时阻断式二次确认,防手快漏看
⑤ 完全移除直接出库模式,所有出库必须按单出库
   - create.vue 删除 radio 切换、direct 相关逻辑
   - 提交前强制校验已选择审批申请单
2026-08-31 09:37:14 +08:00
a4bd6f54d4 feat(outbound): BOM 出库增加清单打印与缺货持久化提醒
- BOM 弹窗物料清单增加「本次结果」列(可出/缺货未出)
- 增加「打印 BOM 清单」按钮,可打印当前 BOM 的完整出库清单
- 缺货物料持久化到 localStorage,下次打开同一 BOM 时自动提示历史缺货记录
  防止重复出库或漏出
- 打印区域使用与 A4 出库单一致的 iframe 打印机制
2026-08-31 09:26:53 +08:00
57a6ab38d1 fix(outbound): 修复选单列表移除按钮排序后索引错位导致删错行
- 表格绑定 sortedSelectedItems(按库位排序),但移除按钮用 $index 操作原始 selectedItems
- 排序后索引与原始数组不一致,导致删除错误的行
- 改为按 uniqueKey 精确定位删除
2026-08-13 17:17:54 +08:00
9c0414b802 fix(outbound): 出库备注改为必填,按单出库自动填充申请原因
- 备注字段新增 required 校验规则
- placeholder 从「可选填」改为「请填写出库原因」
- 按单出库模式选择申请单后,自动将申请原因填入备注
2026-08-07 15:58:59 +08:00
a8a0290cd8 fix: outbound total_amount 改为需权限 outbound_list:total_amount,库管无此权限则隐藏总金额 2026-07-21 09:49:46 +08:00
35690c5b8f fix: 出库列表 permissionMap 基础列改为 null(始终可见),对齐 inbound 模式;单价/小计保留权限控制 2026-07-21 09:39:16 +08:00
5b1cd4d8d0 fix: 扫码出库类型改为必选,移除默认值SALES
- outbound_type 默认值: SALES → '' (空)
- el-select 新增 clearable
- rules 新增 outbound_type required 校验
2026-07-16 13:13:08 +08:00
329820117f perf: 消除 outbound/borrow BOM 匹配的 while(true) 全量加载
## 后端
- outbound.py: 新增 POST /api/v1/outbound/bom-match-stock 端点
  接收 child_ids[],服务端按 base_id IN 查询三表有库存记录并返回

## 前端
- outbound.ts: 新增 bomMatchStock(childIds) API 函数
- Selection.vue: loadAllStockForBom (while(true) 全量) → loadStockForBom (单次 API)
- borrow/apply/index.vue: 同上

## 效果
- BOM 匹配从 ~90 次 HTTP 请求降为 1 次
- 浏览器内存从 ~18000 条降为 ~8-50 条
2026-07-15 17:52:08 +08:00
2556b77530 perf: 系统级性能优化与并发安全修复
## 并发安全修复 (4处)
- scrap.py: 报废执行添加 SELECT FOR UPDATE 悲观锁,消除 TOCTOU 竞态
- stock.py (adjust_stock): 盘点调整添加 for_update=True 行锁
- outbound_service.py: 低库存预警 SMTP 调用移到 commit 之后,避免长事务
- trans_service.py: execute_dispatch 按 (source_table, id) 排序 items,消除死锁风险

## N+1 查询优化 (2处)
- inventory_task.py: _prefetch_inventory_map 单条 UNION ALL+GROUP BY 替代循环内逐条查询(N*4次→2次)
- stock.py (export_stocktake): get_borrowed_qty 批量 GROUP BY 替代逐条 TransBorrow 查询(~18000次→1次)

## BOM 列表性能重构
- bom_service.py: get_bom_list 单条 GROUP BY+string_agg+分页,消除 N+1 循环查询
- bom_service.py: 新增 get_bom_summary (轻量 GROUP BY category+COUNT)
- bom.py: 新增 /api/v1/bom/summary 路由,/list 支持 category 过滤

## Odoo 基础信息懒加载
- base_service.py: 新增 get_odoo_summary (GROUP BY category+COUNT)
- base.py: 新增 /api/v1/inbound/base/odoo-summary 路由
- buyOdoo.vue: 懒加载分组架构 (fetchOdooSummary + loadGroupItems)
- material_base.ts: 新增 getOdooSummary API

## 前端 Bug 修复
- BomManage.vue: 懒加载分组 (fetchBomSummary + loadGroupItems + collapse)
- BomManage.vue: 适配新 API 格式 (res.data.items 替代 res.data)
- buyOdoo.vue: 移除 "点击展开加载" 文字
- Selection.vue + borrow/apply/index.vue: openBomSelect 适配新 API 格式
2026-07-15 17:37:57 +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
8d78ed9306 feat: 全局跨域公司选择器 + 权限页crossDomain开关
- 新增CompanySelector组件: 仅crossDomain权限或SUPER_ADMIN可见
- 权限分配页: 全局系统特权下新增"允许全局跨域"操作权限checkbox
- 入库汇总/出库记录搜索栏嵌入CompanySelector
- PermissionConfig: transformData识别crossDomain为操作权限码
2026-07-15 11:11:49 +08:00
dxc
6a895347d0 V3.51 2026-06-25 15:44:31 +08:00
dxc
c1092407ad V3.50 2026-06-25 15:21:43 +08:00
dxc
f27488e693 V3.39版本推送,出库选单依据BOM子件0的逻辑修改 2026-06-01 09:28:22 +08:00
DXC
c0ab3ce6d2 perf: 消除 BOM 齐套分析的全量库存拉取和 O(N·M) 嵌套循环,改为使用后端返回的 current_stock 2026-05-19 10:07:05 +08:00
dxc
f9dd8b6536 版本变更3.22 2026-05-15 13:03:13 +08:00
dxc
857ff958bc 版本变更3.19 2026-05-14 18:01:28 +08:00