Commit Graph

284 Commits

Author SHA1 Message Date
4cd3eefdf4 feat(borrow,outbound): 默认免审批改造——命中物料需审批才走原流程
- models/base.py 加 is_approval_required 列及 isApprovalRequired 序列化;field_permissions 登记
- 新增 POST /inbound/base/batch-approval(批量设需审批,仿批量质检)
- borrow_service.submit_approval / outbound_service.create_request:明细含需审批物料→须选审批人走原审批;否则创建即 status=1(待库管执行)、不发审批邮件
- 判定按 (name,spec_model) 反查启用物料
2026-09-09 09:31:18 +08:00
9622baf76e fix(borrow,outbound): 记录查看按角色收窄——普通只看自己,库管/主管/超管/跨域看全部
- decorators 新增 is_privileged_viewer(SUPER_ADMIN/SUPERVISOR/WAREHOUSE_MGR 或 crossDomain)
- 借库记录列表:非管理者强制 applicant_id=当前用户
- 出库记录列表+详情:非管理者强制只看自己/无权访问他人单(403)
2026-09-09 09:31:01 +08:00
2b0e335790 fix(inbound/buy): 批号自增改为按物料精准取最近一条
- 新增 get_latest_batch_record_by_base_id:SQL 直接 filter(base_id)+order by in_date/id desc first(),O(1) 不分页
- 新增 GET /inbound/buy/latest-record
- 修复原“拉全表前1000条再前端过滤”导致的历史被截断→批号退回000001/重复入库被拦(如 CCAB0029)
2026-09-08 18:16:04 +08:00
02f74278df feat(bom): 归档业务闭环——状态筛选+归档/取消归档入口
- get_bom_list/get_bom_summary 状态过滤支持 archived,enabled 语义改为“启用且未归档”
- get_bom_list 输出补 is_archived;新增 update_archived 与 POST /bom/archive(整组切换归档、清缓存、审计)
- “未指定版本自动取最新”硬化为仅认启用且未归档(get_bom_detail / get_bom_no_by_parent)
- 前端:状态按钮组加“归档”;列表状态列区分归档;操作列加“归档/取消归档”按钮
2026-09-08 11:13:38 +08:00
91fb6c1a03 feat(bom): 自制件子件选版本落库/强校验 + 独立启停 + 状态筛选
- save_bom 落库 child_bom_no/child_bom_version;自制件必选且须属于启用未归档配方集,外购件置空;跨版本查重纳入引用版本
- get_bom_detail 改为读存储列,空则回退最新启用配方;候选/校验/回退读取均排除 is_archived
- 新增 GET /bom/self-bom-versions(子件候选版本);新增 POST /bom/status 独立启停(update_enabled 清缓存)
- 草稿链路 save_draft/get_draft_detail/publish 持久化引用版本
- get_bom_list/get_bom_summary 支持 status(enabled/disabled) 过滤
2026-09-08 10:14:44 +08:00
220f73acbd feat: 首页全局搜索支持按 SKU 查库位——采购/半成品/成品库存纳入搜索并显示库位 2026-09-07 15:47:25 +08:00
478b90fe50 feat: 出库类型贯穿申请→扫码出库——申请单加 outbound_type,申请时选类型、扫码出库自动带出 2026-09-07 14:01:13 +08:00
02f36b383c fix: 借库归还/报废/借库执行操作人由 JWT 数字 ID 改为用户名(归还人不再显示数字) 2026-09-04 16:10:00 +08:00
808e491fe8 fix: BOM 匹配库存(bom_match_stock)加行级公司隔离(三表按 base 公司过滤,超管/跨域不受限) 2026-09-04 15:57:14 +08:00
b40aa3ab90 fix: 出库选单手动添加库存(/v1/inbound/stock/list)加行级公司隔离(三表按 base 公司过滤,超管/跨域不受限) 2026-09-04 15:55:24 +08:00
2f6027b43c fix: 采购列表字段过滤后补回 base_id,恢复前端批号自动叠加(与 semi 列表一致,修 LICA 同物料二次入库拦截) 2026-09-04 15:33:17 +08:00
b6ecde11dd refactor: warehouse/tree 移除临时打点日志,保留 O(N) build_tree 优化 2026-09-04 15:19:00 +08:00
7e3fc11bf5 chore: warehouse/tree 接口加 3 阶段耗时打点日志(查库/build_tree/序列化) 2026-09-04 15:16:20 +08:00
bc6e97ae97 perf: warehouse build_tree 改为 Map 分组内存组装,消除 O(N²) 递归全扫(tree TTFB 1.7s→0.08s) 2026-09-04 15:10:38 +08:00
3bd19c1ab5 feat: 借库审批支持手动完结——已通过(status=1)单可强制完结,库管/主管/超管可见 2026-09-04 14:52:31 +08:00
7cdc0bc3a1 perf: 库位选择器改为按需懒加载——新增 /warehouse/children 接口,WarehouseSelector 移除全量树依赖 2026-09-04 10:51:49 +08:00
1b760f351e fix: 出库/借库扫码查库存与 BOM 子件物料选择加行级公司隔离(超管/跨域不受限) 2026-09-04 09:40:00 +08:00
bf7b2cc6d3 feat: BOM 库存接口支持按版本查询,修复同一编号多版本下拉回显错乱 2026-09-02 18:39:17 +08:00
4e09860b36 feat: 采购申请完结功能移到批次主行,与出库审批一致
- 移除展开行内的单条完结按钮(需点开),改为主行'完结本批'直接可见
- 后端 batch-approve 支持 action=close(批次内已通过→已完结)
- 超管/库管在主行操作列即可看到并完结
2026-09-01 16:21:48 +08:00
fbb4160787 feat: 采购申请新增库管'完结'功能(仿出库审批)
- 后端:approve 接口支持 action=close(已通过→已完结 status 1→4),模型 status_text 支持已完结
- 前端:采购申请状态tab加'已完结',已通过单显示'完结'按钮(库管)
2026-09-01 16:15:48 +08:00
a2d3a2c43a fix: 半成品列表字段权限过滤后补回 base_id,修复批号自增失效
- get_list 的 apply_strict_rbac 剥离 base_id,导致前端 checkHistoryAndSetMode 无法按物料匹配历史
- 过滤后补回 base_id,扫码/选物料联动时批号能按历史正确自增(如 000002 → 000003)
2026-09-01 16:08:27 +08:00
bee037f925 feat: MOM 扫码入库对接 Track 产品身份证自动填充
- 半成品/成品入库弹窗顶部新增'扫码入库'按钮,打开扫码面板(扫码枪/摄像头)
- 扫码 Track 身份证后自动填充物料信息与序列号,不再二次搜索
- 后端新增 track_query_service(httpx 调 Track lookup)、track-lookup 接口
- 入库成功后 notify_track 通知 Track 闭环
2026-09-01 13:52:56 +08:00
893e7250db fix(api): update-stocktake-quantity 按 session_id 隔离更新,防止跨会话误改
后端 update_stocktake_quantity 原只按 stock_id+source_table 查草稿,
不同盘点会话会相互覆盖实盘数。后端增加 session_id 过滤,
前端 handleQuantityChange 配套传入当前会话ID,API 类型同步补全。
2026-08-31 16:00:20 +08:00
eb017d881c perf(api): draft/list 与 stocktake/all-items 改为数据库级分页,消除 N+1
- /draft/list: 废弃 query.all() + 逐条查库存/material_base 的 N+1,
  改为原生 SQL 连表(草稿 JOIN 三张库存表 + material_base)+ LIMIT/OFFSET 分页
- /stocktake/all-items: 废弃三表 .all() + item.base 懒加载 + 内存切片,
  改为 UNION ALL + LEFT JOIN + LIMIT/OFFSET,COUNT 聚合求总数
2026-08-31 16:00:13 +08:00
6ba20fa3db fix(stocktake): 修复盘库重复记录 — 去重条件去掉 user_id
根因: draft/add 用 (user_id, uuid, session_id) 匹配去重,
同一盘点单多个用户(手机/平板不同账号)操作同一物料时,
每个用户各建一条 → 重复记录。

修复:
- 去重改为 (session_id, uuid) 唯一,不按 user_id 隔离
- 已存在则更新数量并记录最后操作用户
- 清理现有重复数据(保留每组最小id)
- 新增唯一索引 uq_stocktake_draft_session_uuid 防未来再重复

数据库需执行:
DELETE FROM stocktake_draft a USING stocktake_draft b
  WHERE a.session_id=b.session_id AND a.uuid=b.uuid AND a.id>b.id;
CREATE UNIQUE INDEX IF NOT EXISTS uq_stocktake_draft_session_uuid
  ON stocktake_draft(session_id, uuid);
2026-08-31 15:18:47 +08:00
7a3ffd8ba5 feat(stocktake): 重复扫码提示并确认是否修改
问题: 同一个码扫两遍输入两个数,后端 draft/add 直接覆盖,无任何提示
可能误改已盘数量

改进:
- 后端 draft/list 支持 uuid 过滤,可精确查某物料是否已盘
- 扫码弹输入框前,先精确查询该物料是否已盘
- 已盘过 → 弹窗提示『当前实盘为 X,是否修改?』
  确认后预填旧值供修改,取消则不改变
2026-08-31 15:13:52 +08:00
c6e8c887ec perf(stocktake): 应盘清单分页 + 扫码置顶高亮
性能修复(解决打开费劲):
- get_all_stocktake_items 改为分页返回 {items(当前页), total, total_scanned}
  不再三次 .all() 全量加载 + 内存排序
- 前端 fetchAllStockItems 分页拉取(每页200),stats 用后端返回的 total
  不依赖全量数组长度

体验优化:
- 扫码成功后将物品置顶到当前表格第一行并浅绿高亮3秒
  库管无需翻页找刚扫的物品,'嘀'一下确认数量即可继续
- 主表格行高亮样式 .just-scanned-row
2026-08-31 14:59:22 +08:00
7e281cf285 fix(stocktake): 盘库扫码改为精确匹配,修复漏匹配与性能问题
问题:
1. 准确性 bug: 前端用 getStockList({pageSize:10, keyword}) 模糊搜索 + find()
   当 SKU 前缀相同、目标不在前10条时,误报'未找到该物料库存'
2. 性能: 每次扫码全表 ilike %x% 搜索 3 张表,不走索引

修复:
- 后端 get_stock_info 改为精确匹配(==)优先,未命中再回退模糊搜索
- 新增 GET /inbound/stock/scan 扫码精确匹配接口,返回唯一命中
- 前端 onScanSuccess/handleManualInput 改用 scanStockByBarcode
  一次请求直接命中,不再依赖 pageSize:10 + find()
2026-08-31 14:49:24 +08:00
1000deda2f feat(purchase): 采购单号改为 批次-批内序号 格式,显式体现同批
按用户原始逻辑还原并扩展:
- 单号格式: PUR-日期-时间-批次-批内序号
  例: PUR-20260831-1021-0001-0001(今日第1次采购的第1条)
- generate_request_no 支持 batch_seq 参数生成批次+批内序号
- 新增 GET /purchase/next-batch-seq 返回今日下一个批次号
- 前端提交时获取批次号,同批所有行共享,循环提交生成连续批内序号
- 批次识别: 单号去掉末尾 -批内序号 即为批次前缀
- 保留旧格式兼容(无 batch_seq 时回退 PUR-日期-时间-流水)
2026-08-31 13:54:03 +08:00
f499346f74 refactor(purchase): 批次概念改为基于 request_no 前缀推导,撤销数据库字段
按用户反馈:不需要新增数据库字段,现有 request_no (PUR-20260831-1021-0001)
本身就能推导批次——去掉末尾4位流水号即为批次前缀。

- 撤销 purchase_request.batch_no 字段(模型/数据库)
- 撤销 create 接口的 batch_no 参数与 generate_batch_no
- 批次标识 = request_no 去掉末尾 -XXXX 段
  getBatchKey('PUR-20260831-1021-0001') = 'PUR-20260831-1021'
- 按批次查询/批量审批接口改用 batch_key (request_no LIKE 前缀)
- 前端列表批次号列、整批弹窗、一键审批本批均基于前缀推导
- 零数据库迁移,历史数据立即可用
2026-08-31 13:49:11 +08:00
664127ee30 feat(purchase): 采购批次号支持,同一批提交的记录可分组查看/统计
背景: 循环提交把一批物品生成多条独立申请,无法识别'哪些是同一批采购'

后端:
- purchase_request 模型新增 batch_no 字段(同一批提交共享)
- create 接口接收 batch_no,无则自动生成 BATCH-yyyyMMdd-HHmm-随机
- 新增 GET /purchase/batch/<batch_no> 按批次查询所有记录
- 批量审批接口支持按 batch_no 一键审批整批

前端:
- 提交时生成 batch_no 共享给所有行
- 列表新增批次号列(可点击)
- 新增'整批查看'弹窗:显示同批次所有记录 + 一键批量通过本批
- 操作列新增'整批'按钮

数据库需执行: ALTER TABLE purchase_request ADD COLUMN batch_no VARCHAR(100);
2026-08-31 13:46:46 +08:00
4b62c81baa feat(purchase): 采购申请批量审批(解决循环提交需多次审批)
问题: 一次提交 10 个物品生成 10 条独立申请,审批人需操作 10 次

后端:
- 新增 POST /api/v1/purchase/batch-approve 批量审批接口
  接收 ids + action,循环审批,成功/失败分别返回

前端:
- 待审批列表加多选列(仅 status=0 可勾选)
- 顶部批量操作栏:批量通过 / 批量驳回 / 取消选择
- 批量驳回弹窗统一填写驳回原因
2026-08-31 13:41:25 +08:00
36e9f500aa feat(borrow): 借库未归还直接报废功能(关联报废单流程)
后端:
- trans_service 新增 scrap_borrow 方法:未归还借用标记为 scrapped,
  扣减总库存 stock_quantity(可用已在借出时冻结),生成 TransScrap 报废记录
- transactions.py 新增 POST /borrow/scrap 接口(复用 op_return:operation 权限,库管/主管可操作)
- scrap.py query_records 支持 trans_borrow 来源的物料名解析 + 行级隔离

前端:
- records.vue 未归还记录新增「报废」按钮(库管/主管可见)
- 报废弹窗:勾选明细 + 填报废原因 + 二次确认
- 状态显示新增「已报废」标签;整单所有明细报废时标记为 scrapped
2026-08-31 10:21:28 +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
2ff0f92f94 fix(stocktake): 修复盘点报告导出 500 错误
- 导出盘点报告时第1126行使用了 func.sum()/func.coalesce()
  但文件未导入 sqlalchemy 的 func
- 改为 db.func.sum()/db.func.coalesce(),与文件内其他用法一致
2026-08-28 15:53:03 +08:00
22c7d352d6 refactor(import): API 层支持导入策略参数
- /preview 端点接收 mode 参数,update 模式下 DB 重复行不视为错误
- /execute 端点接收 mode 参数传递给 service 层
- execute 仅导入 status 为 success/update 的行,error 行不再被提交
2026-08-03 15:53:15 +08:00
63c36d1466 fix: 借库记录签名改回 null(始终可见),暂等 sys_element 补齐权限码 2026-07-21 09:59:14 +08:00
ca21294580 fix: transactions filter_item_by_permissions 只保留签名过滤,基础字段不再剥离 2026-07-21 09:56:34 +08:00
30f336779e fix: outbound filter_item_by_permissions 只过滤敏感金额字段,基础明细字段不再剥离 2026-07-21 09:53:47 +08:00
d80edc961c fix: 三个模块 update endpoint 统一 Default Deny + 权限码全量对齐 sys_element 2026-07-20 14:26:22 +08:00
3dca2b9254 fix: buy.py update field_to_perm 库位权限码 inbound_buy:warehouse_location → inbound_buy:warehouse_loc,对齐 sys_element 2026-07-20 14:21:22 +08:00
9bcd02c742 fix: update_buy perm_code=None 导致 NoneType 崩溃 + validateUnique 编辑时排除自身改用 Number() 防类型不一致 2026-07-17 16:57:46 +08:00
266a0e011f fix: 防自锁 — 操作者修改自己角色时强制保留 system_permission + system_permission:operation 2026-07-17 15:42:31 +08:00
1cc7d51e03 fix: semi/product submit 响应用 apply_strict_rbac 替代硬编码 resp.pop,成本字段纳入 RBAC 统一管控 2026-07-17 14:36:26 +08:00
61b60f7aa0 fix: 修复 semi/product 物料搜索空白 — search_base 移除错误地对 MaterialBase 数据使用 Stock 表映射过滤 2026-07-17 13:45:28 +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
2e903cff2c perf: 规格连号助手优化 — 流式查询+宽松regex+Redis缓存
## base_service.py get_latest_specs
- .all()全量→with_entities().limit(10000) 防OOM
- regex放宽: 支持 LICA-3000/M3x12 等格式(旧版仅匹配OPT12046)
- 移除OPT硬编码→SUB_CATEGORY_PREFIXES可配置集合
- Redis缓存(1h): 首次查询后缓存,后续命中直接返回

## base.py 缓存失效
- 新增 _invalidate_specs_cache() 辅助函数
- create/update/delete 成功后清除缓存
2026-07-16 18:15:43 +08:00
754c46bd59 feat: 采购申请字段级价格权限 — 库管看不到采购价格
## permission_service.py
- init_all_menus: 新增3个采购权限元素
  inbound_purchase:unit_price, :total_price, :tax_rate

## purchase.py
- _filter_purchase_prices(): 无价格权限则pop价格字段
- GET /purchase (列表): 应用价格过滤
- GET /purchase/<id> (详情): 应用价格过滤
- approved-unstocked: 保留价格(入库数据源,前端按inbound_buy权限控制)

## purchase/index.vue
- 列表: 单价/总价/税率列 v-if hasPermission
- 详情: 单价/总价 v-if hasPermission
2026-07-16 17:34:24 +08:00
5411cabd0a feat: Excel批量导入后端 — 模板下载+预览验证+执行导入
## import_service.py (新增)
- generate_template(): 基础信息/BOM Excel模板生成(带样例数据)
- parse_boolean(): 是/否/True/False 稳健布尔解析
- preview_material/bom(): Dry-Run解析+验证(重复/存在性/必填)
- BOM ffill: 父件列自动向下填充(支持合并单元格UX)
- execute_material/bom_import(): 事务包裹批量写入

## import_api.py (新增)
- GET /api/v1/import/template?type=material|bom
- POST /api/v1/import/preview (文件→预览验证)
- POST /api/v1/import/execute (确认导入)

## __init__.py + requirements.txt
- 注册 import 蓝图; 新增 pandas>=1.5.0
2026-07-16 14:00:28 +08:00
347f20497c feat: Redis幂等锁 + 关键端点防重复提交
## decorators.py
- 新增 @prevent_double_submit(lock_timeout=5) 装饰器
  Redis key: idem:{user_id}:{path}:{MD5(body)}
  锁存在→409, 不存在→setex→执行业务→delete
  Redis不可用时fail-open降级放行

## purchase.py
- POST /purchase (创建): +@prevent_double_submit
- PATCH /purchase/<id>/approve (审批): +@prevent_double_submit

## outbound.py
- POST /outbound (出库): +@prevent_double_submit

## transactions.py
- POST /borrow/dispatch (借库扣减): +@prevent_double_submit
2026-07-16 13:08:37 +08:00