|
|
35690c5b8f
|
fix: 出库列表 permissionMap 基础列改为 null(始终可见),对齐 inbound 模式;单价/小计保留权限控制
|
2026-07-21 09:39:16 +08:00 |
|
|
|
451d7b5719
|
V3.63
|
2026-07-20 14:33:26 +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 |
|
|
|
f6c1dedd22
|
V3.62,库管权限修改内容
|
2026-07-17 17:06:27 +08:00 |
|
|
|
097fbb319f
|
fix: validateUnique 编辑模式下值未变时跳过重复校验,仅值改变时才检查冲突
|
2026-07-17 17:04:10 +08:00 |
|
|
|
1f43de4ccc
|
debug: buy.vue validateUnique 加详细日志——打印 form.id/row.id/value 比对过程
|
2026-07-17 17:02:25 +08:00 |
|
|
|
9bcd02c742
|
fix: update_buy perm_code=None 导致 NoneType 崩溃 + validateUnique 编辑时排除自身改用 Number() 防类型不一致
|
2026-07-17 16:57:46 +08:00 |
|
|
|
a8fb0cfc94
|
debug: validateUnique 加详细日志追踪编辑时误报重复编号问题
|
2026-07-17 16:53:57 +08:00 |
|
|
|
76c7e3d3dc
|
V3.61,出库审批的可编辑即可操作
|
2026-07-17 16:31:33 +08:00 |
|
|
|
29de22b575
|
fix: crossDomain 重复收集 — 真实树中的 system_mgmt/crossDomain 与虚拟 global_privileges 冲突,从真实树中移除 crossDomain 避免双重收集
|
2026-07-17 16:22:59 +08:00 |
|
|
|
0e6294890c
|
debug: crossDomain 收集逻辑加精确断点日志
|
2026-07-17 16:20:22 +08:00 |
|
|
|
c45e7b5a52
|
debug: assign_permissions 加详细日志追踪 crossDomain 删除/插入/提交后状态
|
2026-07-17 16:18:05 +08:00 |
|
|
|
0074d96163
|
fix: 权限配置页 GET 请求加 _t 防浏览器缓存 + 保存后自动刷新 UI
|
2026-07-17 16:13:12 +08:00 |
|
|
|
96eb56766e
|
fix: 回退权限保存后的 auto-refresh,改为手动刷新提示
|
2026-07-17 16:07:54 +08:00 |
|
|
|
c9cf4e0901
|
fix: 权限保存后自动刷新 UI 回显,确保勾选框状态与数据库同步
|
2026-07-17 16:02:51 +08:00 |
|
|
|
de2d13fa68
|
fix: 全局权限码 crossDomain/global:cross_company_op 强制存为 company_name='',防止按公司隔离失效
|
2026-07-17 15:50:06 +08:00 |
|
|
|
266a0e011f
|
fix: 防自锁 — 操作者修改自己角色时强制保留 system_permission + system_permission:operation
|
2026-07-17 15:42:31 +08:00 |
|
|
|
efe04c5d42
|
fix: assign_permissions 删除逻辑对齐 get_user_permissions 查询逻辑 — 公司定制+全局模板一起替换
|
2026-07-17 15:38:14 +08:00 |
|
|
|
9c3fcbca4c
|
migration: 补齐 sys_element 缺失的 :operation 权限码 + 角色分配 SQL
|
2026-07-17 15:29:59 +08:00 |
|
|
|
f3c946d584
|
fix: 借库审批同样修复 — sys_element 补充 op_borrow_approval:operation,can_approve 增加 RBAC 权限判断
|
2026-07-17 15:22:54 +08:00 |
|
|
|
05832e913b
|
fix: can_approve 增加 outbound_approval:operation RBAC 权限判断,前端按钮权限与后端审批权限统一对齐
|
2026-07-17 15:17:26 +08:00 |
|
|
|
686186d7d6
|
fix: Create/Update 成本字段分离 — Create 保留 BOM 自动计算值,Update 按 hasFormFieldPermission 剥离敏感字段
|
2026-07-17 14:50:06 +08:00 |
|
|
|
8b6539b976
|
fix: semi/product 对话框中成本字段添加 v-if 权限守卫,与 buy.vue 的 hasFormFieldPermission 模式对齐
|
2026-07-17 14:43:08 +08:00 |
|
|
|
1cc7d51e03
|
fix: semi/product submit 响应用 apply_strict_rbac 替代硬编码 resp.pop,成本字段纳入 RBAC 统一管控
|
2026-07-17 14:36:26 +08:00 |
|
|
|
cac8a360ec
|
V3.60,邮件发送失败问题修复
|
2026-07-17 14:28:40 +08:00 |
|
|
|
fd506775c1
|
fix: send_email_async 主线程预取 config 传入 daemon 线程,修复异步邮件因丢失 Flask context 导致 MAIL_ENABLED=false
|
2026-07-17 14:27:39 +08:00 |
|
|
|
5b57895b99
|
fix: docker-compose 添加 MAIL_* 环境变量,确保 daemon 线程中邮件配置不丢失
|
2026-07-17 14:27:32 +08:00 |
|
|
|
a4a6d027fe
|
V3.59,紧急修复出入库问题
|
2026-07-17 14:04:18 +08:00 |
|
|
|
036d756fbd
|
fix: 前端 product.vue permissionMap null→显式权限码 + fetchMaterialSuggestions 改用 async/await 修复首次搜索空白
|
2026-07-17 13:45:38 +08:00 |
|
|
|
9fe64847a7
|
fix: 前端 buy/semi permissionMap 数量字段 null→显式权限码,与后端 RBAC 统一对齐
|
2026-07-17 13:45:32 +08:00 |
|
|
|
61b60f7aa0
|
fix: 修复 semi/product 物料搜索空白 — search_base 移除错误地对 MaterialBase 数据使用 Stock 表映射过滤
|
2026-07-17 13:45:28 +08:00 |
|
|
|
08b7674610
|
fix: 后端数量字段纳入RBAC — StockBuy/Semi/Product 的 in_quantity/stock_quantity/available_quantity 从 None 改为显式权限码
|
2026-07-17 13:45:23 +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 |
|
|
|
d647af6bc3
|
fix: Odoo页面排序失效 — 排序参数未传递+缓存未清除
## buyOdoo.vue
- handleSortChange: 排序时清除已展开分组缓存,强制重新加载
- loadGroupItems: 传递 orderByColumn + isAsc 排序参数到API
|
2026-07-16 17:34:30 +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 |
|
|
|
6e064064d5
|
V3.58
|
2026-07-16 14:28:37 +08:00 |
|
|
|
23fc10a25f
|
fix: Day2就绪 — 删除硬化+索引补全+全局异常处理
## base_service.py — MaterialBase删除硬化
- 新增 BomTable 引用检查(bom_usage_count)
- 物料被BOM引用时禁止删除,提示「请先解除BOM绑定」
## models/base.py + bom.py — 缺失索引补全
- material_base: company_name + is_enabled 添加 index=True
- bom_table: is_enabled 添加 index=True
## __init__.py — 全局异常处理
- @app.errorhandler(ValueError) → 400 + 自定义消息
- @app.errorhandler(404/405) → 标准错误
- @app.errorhandler(Exception) → 500 + 通用消息
堆栈仅记录服务器日志,不泄露到前端
|
2026-07-16 14:14:43 +08:00 |
|
|
|
382b461b68
|
feat: 挂载ImportDialog + 修复路由白屏
## 页面挂载
- buyOdoo.vue: 新增「批量导入」按钮(新增旁边), ImportDialog material
- list.vue: 新增「批量导入」按钮, ImportDialog material
- BomManage.vue: 新增「导入BOM」按钮(新建BOM左边), ImportDialog bom
## 白屏修复
- AppMain.vue: 移除 transition mode=out-in 动画包裹(导致首次访问白屏)
改为 :key=route.fullPath 强制创建新实例
|
2026-07-16 14:00:44 +08:00 |
|
|
|
ebe79be9cb
|
feat: ImportDialog组件 — 三步骤批量导入弹窗(拖拽上传+预览+执行)
## ImportDialog.vue (新增)
- Step1: 模板下载 + el-upload drag 拖拽上传
- Step2: el-table 预览验证结果(绿色通过/红色失败+错误详情)
- Step3: 确认导入(事务保护), errorCount>0时按钮disabled
- Props: importType ('material'|'bom'), modelValue
- Emits: success (刷新列表)
|
2026-07-16 14:00:36 +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 |
|
|
|
5b1cd4d8d0
|
fix: 扫码出库类型改为必选,移除默认值SALES
- outbound_type 默认值: SALES → '' (空)
- el-select 新增 clearable
- rules 新增 outbound_type required 校验
|
2026-07-16 13:13:08 +08:00 |
|
|
|
3290f206c6
|
perf: 导出OOM修复(yield_per流式) + 采购模糊匹配安全防护
## base_service.py export_excel
- query.all()全量→yield_per(2000)分块流式读取
- all_rows[]内存收集→write_only=True直写Excel
- all_rows.sort()→ORDER BY数据库排序
- 删除~160行旧Excel样式/脱敏死代码
- 内存: 全量→2000行分块+输出流
## purchase_service.py
- 模糊匹配安全防护:
len(n.strip())>=2 过滤空格/单字符
.limit(500) 防全表返回
|
2026-07-16 13:08:45 +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 |
|
|
|
8c10bb0903
|
feat: 盘点合并列表 — 服务端JOIN+DB分页,消除99999全量加载
## stock.py
- 新增 GET /draft/merged-list: UNION ALL三表+LEFT JOIN draft
数据库级 LIMIT/OFFSET 分页,不再加载全量到Python内存
- 参数: session_id, keyword, status_filter, page, pageSize
- 返回: list, total, total_scanned (已扫去重数)
## stock.ts
- 新增 getDraftMergedList() API函数
## stocktake/index.vue
- fetchInventoryList: 改用merged-list单次调用替代99999全量+find()
- resumeSession: limit 99999→1 先检查存在,再limit 500加载
|
2026-07-16 13:08:29 +08:00 |
|
|
|
e313f575bf
|
perf: 系统级异步邮件 — 消除SMTP阻塞导致的10-15秒UI冻结
## email_service.py
- 新增 send_email_async(): threading.Thread(daemon=True) 包装 send_email()
无需 Flask app_context (send_email仅用os.getenv+smtplib)
- 6处 send_email→send_email_async 替换
## inventory_task.py
- 库存预警 send_email→send_email_async (2处)
|
2026-07-16 11:26:12 +08:00 |
|
|
|
c07f25b646
|
fix: Fail-Closed 字段级安全加固 — 堵住15+端点价格泄露
## stock.py
- _make_price_stripper(): 工厂函数,选单前缀自动剥离所有价格/成本字段
- _do_get_stock_list(permission_prefix): 每个item.to_dict()后剥离价格
- /all 端点: 非AI模式自动剥离价格字段
- /list 端点: permission_prefix='outbound_selection' 传递
## outbound.py
- /bom-match-stock: 返回前按stock_type剥离全部价格/成本字段
- /scan: result.pop('price', None)
## scrap.py
- /scan: result.pop('price', None)
- /records: 每条记录剥离 cost_at_scrap, total_loss
## transactions.py
- filter_item_by_permissions: 从空字典恢复完整20字段映射
- /borrow/stock-list: permission_prefix='op_borrow_apply' 传递
## buy.py / semi.py / product.py
- submit 成功响应剥离所有价格/成本字段(不泄露给前端)
## bom.py
- /base/list: 剥离 referencePrice
|
2026-07-16 11:26:05 +08:00 |
|
|
|
128985af17
|
feat: 采购模块重构 — 权限驱动的可见性 + 严格操作权限
## purchase.py
- GET /purchase (列表): @jwt_required, 有权限看同公司全部/无权限只看自己
- POST /purchase (创建): @jwt_required, 任何登录用户可提交
- GET /purchase/<id> (详情): @jwt_required, 自己的或有权者可见
- PATCH /purchase/<id>/approve (审批): @permission_required('inbound_purchase:operation')
不再硬编码 SUPERVISOR 角色, 有操作权限即可审批
- GET /purchase/approvers, /auto-fill, /search-material: @jwt_required (开放给创建流程)
- 新增 _user_has_purchase_perm() 统一权限检查
- 移除所有硬编码 role in ('SUPERVISOR','SUPER_ADMIN')
## purchase_service.py
- send_email → send_email_async (采购审批邮件异步发送)
## purchase/index.vue
- canApprove: 仅检查 inbound_purchase:operation (菜单权限≠审批权限)
- customUpload: 修复 v-model:file-list 重复推入导致图片双份显示
- auth.py → purchase.py 权限前缀统一
|
2026-07-16 11:25:53 +08:00 |
|
|
|
0b75740ae0
|
feat: 权限系统重构 — 操作权限双向展开 + 启动时自动补全默认权限
## permission_service.py
- init_all_menus: menu_defs 新增 inbound_purchase(采购申请) 菜单项
- init_all_menus: 创建 inbound_purchase:operation 操作元素
- 新增 ensure_default_permissions(): 启动时从 sys_user 提取活跃角色,
对空权限角色自动补全默认菜单(按角色类型分级)
- 自动迁移: 已有 inbound_buy 权限的角色 → 自动获得 inbound_purchase
- SUPERVISOR 启动时自动获得 inbound_purchase 菜单权限
## decorators.py
- _expand_operation_perms 改为双向桥接:
正向(inbound_buy:operation←inbound_buy:write)→通过
反向(inbound_buy←inbound_buy:operation)→通过(可编辑隐含可读)
- 新增详细诊断日志,记录展开路径和失败原因
## __init__.py
- 启动时调用 PermissionService.ensure_default_permissions()
|
2026-07-16 11:25:41 +08:00 |
|