|
|
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 |
|
|
|
29de22b575
|
fix: crossDomain 重复收集 — 真实树中的 system_mgmt/crossDomain 与虚拟 global_privileges 冲突,从真实树中移除 crossDomain 避免双重收集
|
2026-07-17 16:22:59 +08:00 |
|
|
|
c45e7b5a52
|
debug: assign_permissions 加详细日志追踪 crossDomain 删除/插入/提交后状态
|
2026-07-17 16:18:05 +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 |
|
|
|
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 |
|
|
|
1cc7d51e03
|
fix: semi/product submit 响应用 apply_strict_rbac 替代硬编码 resp.pop,成本字段纳入 RBAC 统一管控
|
2026-07-17 14:36:26 +08:00 |
|
|
|
fd506775c1
|
fix: send_email_async 主线程预取 config 传入 daemon 线程,修复异步邮件因丢失 Flask context 导致 MAIL_ENABLED=false
|
2026-07-17 14:27:39 +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 |
|
|
|
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 |
|
|
|
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 |
|
|
|
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 |
|
|
|
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 |
|
|
|
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 |
|
|
|
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 |
|
|
|
0651eb07fa
|
fix: 彻底解耦出库/借库权限联动 + 路由去硬编码roles + 补API权限保护
根因: 借库选单页面14处硬编码outbound_selection:operation, 导致两模块权限联动
修复:
- borrow/apply: 14处outbound_selection:operation→op_borrow_apply:operation
- stock.py: 拆分_do_get_stock_list裸逻辑, 出库/借库各绑独立权限码
- transactions: 新增/borrow/stock-list端点(@permission(op_borrow_apply))
- transaction.ts: 新增getBorrowStockList前端API函数
- outbound.py: 出库审批4端点改用独立outbound_approval权限码
- transactions.py: 借库审批3端点补@permission(op_borrow_approval)
- purchase.py: 采购管理7端点补@permission(inbound_buy)
- audit.py: 审计日志补@permission(system_audit)
- router: 清除全部6处硬编码roles, 交由动态权限树控制
|
2026-07-15 14:33:11 +08:00 |
|
|
|
9988d1b7eb
|
fix: 补全审计/出库审批/采购管理/借库审批的权限保护+公司隔离
- audit.py: 补@permission_required(system_audit)+import, 审计日志通过split_part关联用户表过滤公司
- outbound.py: 出库审批5个端点全补@permission_required(outbound_list)
- purchase.py: 采购管理7个端点全补@permission_required(inbound_buy)
- borrow_service: 借库审批列表通过applicant_id关联用户表过滤公司
- outbound_service: 出库审批列表通过applicant_id关联用户表过滤公司
- purchase_service: 采购列表通过base_id+requester_id双路过滤公司
- base_service/search_material: 去除debug日志
- decorators: 去除debug日志
|
2026-07-15 11:49:53 +08:00 |
|
|
|
e1417d740a
|
feat: 全模块公司隔离 + crossDomain权限码动态跨域控制
- get_current_company_filter: 新增_has_cross_domain_permission, 权限码替代硬编码
- 补全11个Service的get_current_company_filter调用(semi/product/service/outbound/bom/trans/scrap/summary)
- base/search修复: search_material此前无隔离, 已补全
- get_current_company_filter兜底: JWT缺company_name时返回__NO_COMPANY__防止放行
- permission.py: _get_operator_company补全返回值, 修复权限页保存逻辑
- 新增crossDomain迁移脚本, element_type=element挂system_mgmt下
|
2026-07-15 11:11:40 +08:00 |
|
|
|
a2a7e0125c
|
fix: SUPERVISOR不能修改/删除SUPER_ADMIN账号, 不能删自己
|
2026-07-15 09:22:58 +08:00 |
|
|
|
9cae1cc44c
|
feat: 跨域访问从硬编码改为权限码crossDomain动态控制, 新增SQL迁移脚本
|
2026-07-15 09:22:58 +08:00 |
|
|
|
d75c301556
|
fix: batch_create_user 补上缺失的 permission_required(system_user:operation)
|
2026-07-15 09:22:58 +08:00 |
|
|
|
68fd93b457
|
fix: BOM跳转恢复 + 采购单导入优化 + 基础信息表单禁用守卫
- BOM: 移除父件/子件跳转按钮的 isReadOnlyMode 限制, 任何模式均可跳转
- BOM: 补全缺失的5个权限码(sys_element + sys_role_permission)
- 采购单导入: supplier_link→detail_link, 新增采购人/邮箱自动填充
- 采购单导入: 实时搜索(300ms防抖)+模糊匹配, 保留搜索按钮
- 采购单导入: 移除原始/详情链接的历史 autocomplete
- 基础信息: el-form 增加 formDisabled, 无 material_list:operation 时全局禁用
- /approved-unstocked API 增加 permission_required(inbound_buy)
- PurchaseRequest.to_dict() 增加 requester_email
|
2026-07-14 17:02:07 +08:00 |
|
|
|
538b6cb818
|
feat: 采购单导入自动填充采购人/邮箱/详情链接 + 实时搜索
- PurchaseRequest.to_dict() 新增 requester_email
- 导入时自动填入: 采购人(requester_name) + 邮箱(requester_email) + 详情链接(supplier_link)
- 移除原始链接/详情链接的历史 autocomplete, 改为纯输入框+权限守卫
- 采购单导入搜索改为实时输入搜索(300ms防抖), 保留搜索按钮
|
2026-07-14 16:29:19 +08:00 |
|
|
|
8f468a0a39
|
fix: 修复全局字段权限导致数据显示为空的问题
根因: /permissions/role/<role_code> 接口要求 system_permission,
非管理员角色无法获取自身权限 → 权限数组为空 → 前后端字段全被过滤
修复:
- permission.py: 查自己角色不再需要 system_permission, 解除权限死锁
- 5个 inbound API: 恢复完整 field_to_perm 映射, 每个字段均可独立权限管控
- 补齐遗漏字段(qty_inbound/qty_stock/qty_available/request_id/request_no)
- buy.vue: 入库表单价格字段加入 hasFormFieldPermission 守卫
|
2026-07-14 16:09:41 +08:00 |
|
|
|
a11b7972c3
|
feat: 打通采购申请与入库的按单入库链路
后端变更:
- PurchaseRequest 新增 base_id 硬关联 MaterialBase,StockBuy 新增 request_id 关联采购单
- handle_inbound 支持 request_id 入参,入库后自动反写采购单状态为已完成
- 新增 get_approved_requests 接口,返回已审批未入库采购单列表(含物料信息+历史数据回退匹配)
- create_purchase_request 新增 name+spec_model 自动匹配 MaterialBase
- 新增 SQL 和 Alembic 数据库迁移脚本
前端变更:
- 入库表单新增"从采购单导入"弹窗,支持搜索/选中已审批采购单并一键填充物料和商务信息
- 单价/总价交叉推算,缺项自动补全
- 选中行蓝色高亮+点击整行选中,行级交互优化
|
2026-07-14 15:25:00 +08:00 |
|
|
|
a2f9a3e754
|
V3.54
|
2026-07-13 17:33:16 +08:00 |
|
|
|
760f78e016
|
feat: 添加参考价格列 + 修复公司筛选跨域问题 + CLIP模型持久化
## 新增功能
- material_base 表新增 reference_price 列(NUMERIC(10,2))
- 基础信息 list.vue / buyOdoo.vue 页面增加「参考价格」列展示和编辑
- 新增 material_list:referencePrice 权限元素,支持按角色控制可见性
## Bug 修复
- 入库三页面(buy/product/semi)公司筛选:非超管用户 company=ALL 不再传给旧版后端
- 基础信息两页面(list/buyOdoo):buyOdoo 增加 v-if=isSuperAdmin 与 list.vue 行为统一
- company=ALL 默认值在各页面 getList/fetchData 中自动过滤,兼容旧版后端
## 运维优化
- docker-compose.prod.yml 增加 models_prod 卷挂载,CLIP模型持久化免重复上传
- deploy_code.sh 增加 models_prod 目录检查与模型文件存在性告警
|
2026-07-13 17:30:52 +08:00 |
|
|
|
4f5965db02
|
feat: JWT多租户数据权限隔离 & 主管系统管理权限 & 含税单价补齐
## 多租户公司数据隔离
- 新增 get_current_company_filter() 工具函数 (decorators.py)
SUPER_ADMIN: 可传company_name参数过滤或传ALL看全量
其他角色: 强制隔离到JWT中的company_name
- 重构 base_service.py / buy_service.py: 用集中式函数替换内联公司过滤
- SysRolePermission 表新增 company_name 字段,支持同角色不同公司权限
- get_user_permissions() 新增 company_name 参数,查公司定制+全局模板权限
- permission.py API 新增 @permission_required 拦截 + 公司过滤
- 19个API/service文件传递 company_name 到权限查询
## 主管系统管理权限
- delete_user() 允许SUPERVISOR删除同公司用户 (原仅SUPER_ADMIN)
- get_all_users() 新增 company_name 参数过滤
- 用户列表/权限分配 API 应用 get_current_company_filter()
- 前端 UserCreate.vue: 超管可见公司下拉框,主管隐藏部门字段
## 前端多租户适配
- material/list.vue / buy.vue: 公司下拉框仅超管可见,默认ALL
- UserCreate.vue: 新增搜索栏公司筛选,部门字段按角色显隐
- auth.ts: getUserList() 支持 params 参数
## Bug修复: 含税单价字段补齐
- buy.vue: 表格列/高级筛选/排序/权限映射新增 post_tax_unit_price
- buy_service.py: allowed_fields/sort_field_map 新增 post_tax_unit_price
|
2026-07-13 15:12:22 +08:00 |
|
|
|
ffafa635bb
|
feat: 将产品图/说明书备注字段从JSON数组中分离为独立数据库列
- 数据库新增 product_image_remark 和 manual_link_remark 两个TEXT列
- 备注文字不再与上传文件URL混存在 generalImage/generalManual JSON数组中
- 前端输入框改为textarea,绑定独立的备注字段
- 修复 handlePasteLink 忽略field参数的bug
- 新增 isInternalFile 辅助函数,用于区分上传文件与纯文本
- 同步修改 list.vue 和 buyOdoo.vue 两个页面
|
2026-07-10 10:52:23 +08:00 |
|
|
|
c1092407ad
|
V3.50
|
2026-06-25 15:21:43 +08:00 |
|
|
|
1450e6c1de
|
fix(借还记录列表): 按 borrow_no 单号维度分页 + 修 SQLAlchemy Row 适配错误
- 分页基准从明细行改为单号:21 项单号不再被拆到 3 页
- 步骤 1a 构造 GROUP BY borrow_no 的 subquery(sort_key + status 聚合)
- 步骤 2 主查询 SELECT order_subq.c.borrow_no 一列,避免触发 PG GROUP BY 严格模式 (f405)
- 步骤 3 用 page_borrow_nos 拉明细,保留前端 groupMap 期望的 items 形态
- pagination.items 用 isinstance + hasattr(_mapping) 兜底提取纯字符串(修 psycopg2 can't adapt type 'Row')
- service 加 try-except,路由层识别 500 透传 traceback
- status 过滤改为单号聚合(borrowed=至少一条未还,returned=全部归还)
|
2026-06-16 14:53:42 +08:00 |
|
|
|
b79b0f99af
|
fix(借库扫码出库): 撤销 joinedload 修复 PG "FOR UPDATE cannot be applied to nullable side of outer join"
- 83b3db6 引入的 joinedload(ModelClass.base) 触发 LEFT OUTER JOIN,
而 with_for_update() 会被 SQLAlchemy 透传到 join 的 nullable 侧,
PG 直接抛 FeatureNotSupported,且连表加锁有死锁风险
- 退回最安全的单表 FOR UPDATE 模式,接受 N+1 lazy 加载的代价
- 在 防线3 上方加防回归注释,明确禁止未来再加 joinedload
- process_return 中的另两处 joinedload 不带 FOR UPDATE,不受 PG 限制,保留
|
2026-06-16 13:56:11 +08:00 |
|
|
|
83b3db693a
|
fix(借库扫码出库): 校验 key 从 (source_table, sku) 改为 (name, spec_model) + N+1 修复
- 借库申请按 (name, spec_model) 发起,审批明细无 sku 字段;
旧代码用 sku 做 key 会导致所有条目坍塌到同一桶,校验形同虚设
- 改为在扫码循环内即时累加、即时拦截:
防线4 锁定 stock 行后从 material_base 取真实 (name, spec_model),
与审批单按 strip 后的 (name, spec_model) 聚合比对
- 新增 joinedload(ModelClass.base) 一次 JOIN 加载 base,
避免循环内 stock.base 触发 N+1
- 修正 dispatch_borrow docstring 中"sku 用于超额交叉校验"的错误描述
|
2026-06-16 13:50:49 +08:00 |
|
|
|
bfeb397c4a
|
fix(借库扫码出库): items 字段名 stock_id → id 修复 400 + dispatch_borrow docstring 补全 sku 字段说明
|
2026-06-16 13:38:51 +08:00 |
|
|
|
5c0c1632c3
|
fix(审批邮件): items_json序列化Bug修复 + 邮件方法出库/借库物理隔离
|
2026-06-12 15:04:57 +08:00 |
|
|
|
7ef22a3830
|
feat(借库审批流): 完整前后端实现
|
2026-06-12 14:08:19 +08:00 |
|