feat(gerp): 部门新增成员改用部门员工关联选择器
UserRoleDepartment 表单人员输入统一为 DEPT_EMPLOYEE_PICKER(与 eHRM/工作组同源单一配置):新增虚拟列置顶,用户/角色列改为纯列表展示,DeptUUID 表单隐藏(上下文优先,否则取选择器解析的部门),删除就地写的 UserRoleSearch 选择器;add_record/modify_record 支持 UserRoleDepartmentUUID 解析 UserRoleUUID 并拒绝选到别的员工。
This commit is contained in:
1 parent
4669a7c53c
commit
fd56a9e4ff
2 files changed
+79
-59
No files matched your search
@@ -109,16 +109,24 @@
|
||||
|
||||
| 常量 | valueField | 附加行为 | 使用点 |
|
||||
|------|-----------|---------|--------|
|
||||
| `DEPT_TREE_GROUP` | —(公共 treeGroup) | 部门树分组 + 未分配部门兜底 | 下列两处 + `UserRoleDepartment.UserRoleUUID` |
|
||||
| `DEPT_EMPLOYEE_PICKER` | `UserRoleDepartmentUUID` | 单选 | `EmployeeProfile.UserRoleDepartmentUUID`(eHRM 员工档案) |
|
||||
| `DEPT_TREE_GROUP` | —(公共 treeGroup) | 部门树分组 + 未分配部门兜底 | 被下面两个常量引用(勿单独就地使用) |
|
||||
| `DEPT_EMPLOYEE_PICKER` | `UserRoleDepartmentUUID` | 单选 | `EmployeeProfile.UserRoleDepartmentUUID`(eHRM 员工档案)、`UserRoleDepartment.UserRoleDepartmentUUID`(部门新增成员,虚拟列) |
|
||||
| `DEPT_EMPLOYEE_PICKER_MULTI` | `UserRoleDepartmentUUID` | 多选暂存 + 成员/负责人类型 | `UserRoleWorkgroup.UserRoleDepartmentUUID`(工作组新增成员,虚拟列) |
|
||||
|
||||
**两个使用点取值一致**(都是「部门员工关联」= 部门 + 该用户在本公司的用户-角色对),
|
||||
区别只在是否多选暂存;落库时由 `FormService._ResolveDeptEmployee()` **统一解析**(唯一实现):
|
||||
**三个使用点取值一致**(都是「部门员工关联」= 部门 + 该用户在本公司的用户-角色对),
|
||||
区别只在单/多选;落库时由 `FormService._ResolveDeptEmployee()` **统一解析**(唯一实现):
|
||||
|
||||
- `EmployeeProfile`:取 `UserUUID` / `DeptUUID` / `DeptName` 三列落库(常量 `_PROFILE_DEPT_EMPLOYEE_KEYS` 声明)
|
||||
- `UserRoleWorkgroup`:取 `UserRoleUUID` 落库到关系列(关系表只存用户-角色对,**部门仅用于选择/分组展示**);
|
||||
表单里该关系列 `hideInForm`,由虚拟列 `UserRoleDepartmentUUID` 作为唯一人员选择入口
|
||||
- `UserRoleDepartment`:取 `UserRoleUUID` 落库;`DeptUUID` **表单上下文优先**(部门详情页 = 当前部门),
|
||||
无上下文时取选择器解析出的部门(员工详情页新增"部门职务"即此场景);
|
||||
员工详情页上下文里 `UserUUID` 由 query 注入,选择器选到别的员工会被后端拒绝
|
||||
(`所选「部门员工关联」不属于当前员工`)。两张关联表的 `UserUUID`/`RoleUUID` 列一律
|
||||
`editable=False`(纯列表展示),人员输入只有选择器一个入口
|
||||
|
||||
> 已知取舍:选择器数据源是 `UserRoleDepartment`,因此**尚未挂靠任何部门的"用户-角色对"
|
||||
> 选不到**。公司员工列表(`UserRole`)新增/编辑时若填了部门会同步建主关联,正常路径不受影响。
|
||||
|
||||
**唯一性约束**:员工档案一人一份 —— 新增与改绑均由 `form_service.add_record/modify_record` 拦截
|
||||
(前端在已有档案时隐藏新增入口)。
|
||||
|
||||
@@ -124,7 +124,8 @@ def create_tenant_admin_role(tenant):
|
||||
# (落库时再解析出 UserUUID / DeptUUID / DeptName,一人一份档案)
|
||||
# 2) UserRoleWorkgroup.UserRoleDepartmentUUID(工作组新增成员):多选暂存批量新增,
|
||||
# 落库时解析出 UserRoleUUID(关系表只存"用户-角色对",部门仅用于选择/分组展示)
|
||||
# 3) UserRoleDepartment.UserRoleUUID(部门挂靠,表单隐藏):仅复用 DEPT_TREE_GROUP
|
||||
# 3) UserRoleDepartment.UserRoleDepartmentUUID(部门新增成员):单选,
|
||||
# 落库时解析出 UserRoleUUID(部门取表单上下文注入的 DeptUUID,选择器的部门只用于分组)
|
||||
|
||||
#: 「按部门树分组」浏览面板的公共 treeGroup 配置(EntityPicker):
|
||||
#: 下拉里先展示部门树、点部门看该部门成员,未挂部门的归入 ungroupedTitle
|
||||
@@ -704,14 +705,16 @@ class FormService(FormServiceBase):
|
||||
if table_name == 'UserRole' and pascal_name == 'CompanyUUID':
|
||||
col['visible'] = False
|
||||
# UserRoleDepartment 关联表冗余字段:仅用于隔离过滤与上下文注入。
|
||||
# DeptUUID 列表展示部门(回显部门名),表单可编辑(无 query 注入时(如员工详情页
|
||||
# 新增成员)用户自选部门;详情页场景由 query 注入自动从表单排除);TenantUUID/CompanyUUID
|
||||
# 始终内部注入,表单隐藏
|
||||
# DeptUUID 列表展示部门(回显部门名),表单隐藏 —— 人员输入统一走
|
||||
# "部门员工关联"选择器:部门详情页由 query 注入当前部门,员工详情页则取
|
||||
# 选择器解析出的部门(add_record 里 DeptUUID 表单上下文优先);
|
||||
# TenantUUID/CompanyUUID 始终内部注入,表单隐藏
|
||||
if table_name == 'UserRoleDepartment' and pascal_name == 'DeptUUID':
|
||||
col['title'] = '部门'
|
||||
col['type'] = 'entityPicker'
|
||||
col['entityPicker'] = {**self.ENTITY_PICKER_MAP['Department'], 'placeholder': '选择部门'}
|
||||
col['editable'] = True
|
||||
col['editable'] = False
|
||||
col['hideInForm'] = True
|
||||
col['visible'] = True
|
||||
if table_name == 'UserRoleDepartment' and pascal_name in ('TenantUUID', 'CompanyUUID'):
|
||||
col['editable'] = False
|
||||
@@ -728,29 +731,14 @@ class FormService(FormServiceBase):
|
||||
col['type'] = 'entityPicker'
|
||||
col['entityPicker'] = {**self.ENTITY_PICKER_MAP['PositionCategory'], 'placeholder': '选择绑定的通用部门'}
|
||||
col['editable'] = True
|
||||
# UserRoleDepartment 的 UserRoleUUID:仅列表展示"角色名(用户名)",
|
||||
# 表单隐藏——改用下方两级级联"用户→角色"选择
|
||||
# (UserRoleWorkgroup 的 UserRoleUUID 同为其关系列,表单改由虚拟列
|
||||
# UserRoleDepartmentUUID 的"部门员工关联"选择器承担,见该表的虚拟列配置)
|
||||
if table_name == 'UserRoleDepartment' and pascal_name == 'UserRoleUUID':
|
||||
col['type'] = 'entityPicker'
|
||||
# 复用公共部门树分组配置(取值仍是 UserRoleUUID,数据源为 UserRoleSearch)
|
||||
col['entityPicker'] = {
|
||||
'api': '/API/FormAPI/UserRoleSearch',
|
||||
'labelField': 'UserName',
|
||||
'secondaryField': 'RoleName',
|
||||
'valueField': 'UserRoleUUID',
|
||||
'placeholder': '选择部门员工关联(按部门分组展示)',
|
||||
'treeGroup': dict(DEPT_TREE_GROUP),
|
||||
}
|
||||
col['editable'] = True
|
||||
col['hideInForm'] = True
|
||||
col['entityPicker'] = {**col['entityPicker'], 'dependCompany': True}
|
||||
# UserRoleDepartment 列表可读性:UserRoleUUID 关联列列表隐藏(表单仍隐藏),
|
||||
# 用户/角色由下方虚拟列(UserUUID/RoleUUID)分开展示(值经 get_table 反查 UserRole 注入);
|
||||
# UserRoleDepartment 列表可读性:UserRoleUUID 是"用户-角色对"外键列,列表与表单
|
||||
# 都隐藏 —— 用户/角色由下方虚拟列(UserUUID/RoleUUID)分开展示(值经 get_table
|
||||
# 反查 UserRole 注入),人员输入由虚拟列 UserRoleDepartmentUUID 的"部门员工关联"
|
||||
# 选择器承担(原先就地写的 UserRoleSearch 选择器已删除,避免同一语义两套配置);
|
||||
# PositionUUID 列标题改"职务"(值为职务名,由 _EnrichEntityLabels 回显)
|
||||
if table_name == 'UserRoleDepartment' and pascal_name == 'UserRoleUUID':
|
||||
col['visible'] = False
|
||||
col['hideInForm'] = True
|
||||
if table_name == 'UserRoleDepartment' and pascal_name == 'PositionUUID':
|
||||
col['title'] = '职务'
|
||||
# UserRoleWorkgroup 列表可读性:与 UserRoleDepartment 对齐——
|
||||
@@ -1042,13 +1030,12 @@ class FormService(FormServiceBase):
|
||||
columns = columns[:_idx + 1] + [_dept_col, _pos_col] + columns[_idx + 1:]
|
||||
else:
|
||||
columns = columns + [_dept_col, _pos_col]
|
||||
# 虚拟列"用户/角色":UserRoleDepartment 承担新增表单两级级联(用户选好后,
|
||||
# 角色只显示该用户已绑定且属于目标公司的角色,RoleSearch 级联过滤;提交时后端
|
||||
# 从 (用户, 角色) 解析出 UserRole 关联写入关联表,不重复新增 UserRole);
|
||||
# UserRoleWorkgroup 列表同样拆分展示用户/角色(值经 get_table 反查 UserRole 注入),
|
||||
# 其新增表单用 UserRoleDepartmentUUID"部门员工关联"选择器(见下方虚拟列)
|
||||
# 虚拟列"用户/角色":两张关联表都要把"用户-角色对"拆成两列展示
|
||||
# (值经 get_table 反查 UserRole 注入,关联表本身只存 UserRoleUUID)。
|
||||
# 这两列只用于**列表展示**(editable=False → 任何表单都不出现),
|
||||
# 人员输入统一走 UserRoleDepartmentUUID"部门员工关联"选择器(见下方虚拟列):
|
||||
# 部门新增成员与工作组新增成员同口径
|
||||
if table_name in ('UserRoleDepartment', 'UserRoleWorkgroup'):
|
||||
is_ur_dept = table_name == 'UserRoleDepartment'
|
||||
virtual_cols = [
|
||||
{
|
||||
'field': 'UserUUID',
|
||||
@@ -1056,12 +1043,11 @@ class FormService(FormServiceBase):
|
||||
'titleEn': 'UserUUID',
|
||||
'type': 'entityPicker',
|
||||
'entityPicker': {**self.ENTITY_PICKER_MAP['User'], 'placeholder': '用户'},
|
||||
'editable': is_ur_dept,
|
||||
'editable': False,
|
||||
'sortable': False,
|
||||
'filterable': False,
|
||||
'required': is_ur_dept,
|
||||
'required': False,
|
||||
'visible': True, # 列表展示"用户"(值经 get_table 反查注入)
|
||||
**({'hideInEditForm': True} if is_ur_dept else {}), # 级联列仅新增表单显示
|
||||
},
|
||||
{
|
||||
'field': 'RoleUUID',
|
||||
@@ -1074,12 +1060,11 @@ class FormService(FormServiceBase):
|
||||
'dependCompany': True,
|
||||
'placeholder': '该用户绑定的角色',
|
||||
},
|
||||
'editable': is_ur_dept,
|
||||
'editable': False,
|
||||
'sortable': False,
|
||||
'filterable': False,
|
||||
'required': is_ur_dept,
|
||||
'required': False,
|
||||
'visible': True, # 列表展示"角色"(值经 get_table 反查注入)
|
||||
**({'hideInEditForm': True} if is_ur_dept else {}), # 级联列仅新增表单显示
|
||||
},
|
||||
{
|
||||
# 角色覆盖名(UserRole.RoleName):与"角色"分列展示,仅列表可见
|
||||
@@ -1111,16 +1096,21 @@ class FormService(FormServiceBase):
|
||||
'visible': True,
|
||||
'multiValue': True, # 前端多选:值/回显为逗号分隔的多个职务
|
||||
})
|
||||
# UserRoleWorkgroup 的"部门员工关联"虚拟列(表单里唯一的人员选择入口):
|
||||
# 选一条"部门 + 用户-角色对"的关联,落库时后端解析出 UserRoleUUID
|
||||
# (关系表只存用户-角色对;与 eHRM 员工档案共用 DEPT_EMPLOYEE_PICKER* 单一来源)
|
||||
if table_name == 'UserRoleWorkgroup':
|
||||
# "部门员工关联"虚拟列(表单里唯一的人员选择入口):选一条"部门 + 用户-角色对"
|
||||
# 的关联,落库时后端解析出 UserRoleUUID(关系表只存用户-角色对;部门仅用于分组
|
||||
# 展示,或由表单上下文注入的 DeptUUID 决定)。与 eHRM 员工档案共用
|
||||
# DEPT_EMPLOYEE_PICKER* 单一来源,两张关联表只在"是否多选暂存"上不同:
|
||||
# UserRoleWorkgroup —— 多选暂存,一次批量加入工作组
|
||||
# UserRoleDepartment —— 单选,一次新增一名部门成员
|
||||
virtual_cols.append({
|
||||
'field': 'UserRoleDepartmentUUID',
|
||||
'title': '部门员工关联',
|
||||
'titleEn': 'UserRoleDepartmentUUID',
|
||||
'type': 'entityPicker',
|
||||
'entityPicker': dict(DEPT_EMPLOYEE_PICKER_MULTI),
|
||||
'entityPicker': dict(
|
||||
DEPT_EMPLOYEE_PICKER_MULTI if table_name == 'UserRoleWorkgroup'
|
||||
else DEPT_EMPLOYEE_PICKER
|
||||
),
|
||||
'editable': True,
|
||||
'sortable': False,
|
||||
'filterable': False,
|
||||
@@ -1348,6 +1338,19 @@ class FormService(FormServiceBase):
|
||||
r['UserRoleUUID_label'] = urw_info[_uid]['RoleName']
|
||||
r['UserRoleUUID_UserName'] = urw_info[_uid]['UserName']
|
||||
r['RoleNameCover'] = urw_info[_uid]['RoleNameCover']
|
||||
# UserRoleDepartment 编辑表单"部门员工关联"选择器回显:本行的
|
||||
# UserRoleDepartmentUUID 就是取值本身,标签与选择器选项格式一致
|
||||
# (DeptEmployeeSearch 的 DeptEmployeeLabel = "部门名 · 用户名")
|
||||
if table_name == 'UserRoleDepartment':
|
||||
for r in records:
|
||||
_urd_key = r.get('UserRoleDepartmentUUID')
|
||||
if not _urd_key:
|
||||
continue
|
||||
_dn = r.get('DeptUUID_label') or ''
|
||||
_un = r.get('UserUUID_label') or ''
|
||||
_rd_label = f'{_dn} · {_un}' if (_dn and _un) else (_dn or _un)
|
||||
if _rd_label:
|
||||
r['UserRoleDepartmentUUID_label'] = _rd_label
|
||||
# UserRoleDepartment 职务多值聚合(身兼数职):PositionUUID 从子表取,
|
||||
# 列表值/回显为逗号分隔(前端 multiValue 拆多标签展示)
|
||||
if table_name == 'UserRoleDepartment':
|
||||
@@ -1795,20 +1798,27 @@ class FormService(FormServiceBase):
|
||||
create_data['RoleName'] = None
|
||||
# UserRoleWorkgroup / UserRoleDepartment 规则拦截:新增"用户-角色对 → 工作组/部门"关系时,
|
||||
# 支持三种提交方式:
|
||||
# 1) 直接提交 UserRoleUUID(旧路径,兼容)
|
||||
# 2) 前端"用户→角色"两级级联:提交 UserUUID + RoleUUID,后端解析出 UserRole 关联
|
||||
# 3) 前端"部门员工关联"选择器:提交 UserRoleDepartmentUUID,后端解析出 UserRole 关联
|
||||
# (工作组新增成员;部门仅用于选择,不落库)
|
||||
# 1) 前端"部门员工关联"选择器:提交 UserRoleDepartmentUUID,后端解析出 UserRole 关联
|
||||
# (两张表的表单唯一入口:工作组多选批量新增、部门单选新增成员;
|
||||
# UserRoleDepartment 的部门优先取表单上下文注入的 DeptUUID,否则取选择器解析结果)
|
||||
# 2) 前端"用户→角色"两级级联:提交 UserUUID + RoleUUID,后端解析出 UserRole 关联(兼容旧调用)
|
||||
# 3) 直接提交 UserRoleUUID(旧路径,兼容)
|
||||
# 校验 UserRole 关联存在,且避免同一用户-角色对重复挂同一工作组/部门
|
||||
if table_name in ('UserRoleWorkgroup', 'UserRoleDepartment'):
|
||||
if (table_name == 'UserRoleWorkgroup'
|
||||
and (data.get('UserRoleDepartmentUUID') or data.get('UserRoleDepartmentUUID_id'))):
|
||||
if data.get('UserRoleDepartmentUUID') or data.get('UserRoleDepartmentUUID_id'):
|
||||
# 选择器提交的"部门员工关联"优先(其值就是本次要绑定的人员)
|
||||
_urw_de, _urw_de_err = self._ResolveDeptEmployee(data)
|
||||
if _urw_de_err:
|
||||
return {'error': _urw_de_err, 'field': 'UserRoleDepartmentUUID'}
|
||||
if _urw_de and _urw_de.get('UserRoleUUID'):
|
||||
_ctx_user = _to_uuid_bytes(data.get('UserUUID'))
|
||||
if (table_name == 'UserRoleDepartment' and _ctx_user
|
||||
and _urw_de.get('UserUUID') and _urw_de['UserUUID'] != _ctx_user):
|
||||
# 员工详情页新增"部门职务":员工由上下文固定,选择器不得选到别人
|
||||
return {'error': '所选「部门员工关联」不属于当前员工', 'field': 'UserRoleDepartmentUUID'}
|
||||
create_data['UserRoleUUID'] = _urw_de['UserRoleUUID']
|
||||
# 部门:表单上下文注入的 DeptUUID 优先(部门详情页新增成员),否则用选择器解析出的部门
|
||||
if table_name == 'UserRoleDepartment' and not create_data.get('DeptUUID'):
|
||||
create_data['DeptUUID'] = _urw_de['DeptUUID']
|
||||
urw_ur = create_data.get('UserRoleUUID')
|
||||
target_label = '工作组' if table_name == 'UserRoleWorkgroup' else '部门'
|
||||
urw_target = (create_data.get('WorkgroupUUID') or create_data.get('WorkgroupUUID_id')
|
||||
@@ -1819,10 +1829,8 @@ class FormService(FormServiceBase):
|
||||
if urw_ur is None:
|
||||
# 走"用户→角色"级联:从 (用户, 角色) 解析 UserRole 关联(同公司约束)
|
||||
if not urw_user or not urw_role:
|
||||
if table_name == 'UserRoleWorkgroup':
|
||||
return {'error': '请选择「部门员工关联」(或直接指定用户与角色)',
|
||||
return {'error': f'请选择「部门员工关联」({target_label}成员须绑定一条部门员工关联)',
|
||||
'field': 'UserRoleDepartmentUUID'}
|
||||
return {'error': f'必须指定用户、角色和{target_label}', 'field': 'UserRoleUUID'}
|
||||
try:
|
||||
ur_obj = RoleManager.GetUserRoleRelation(urw_user, urw_role, urw_company)
|
||||
except Exception:
|
||||
@@ -2115,15 +2123,19 @@ class FormService(FormServiceBase):
|
||||
# 工单状态流转:保存修改前的 Status,用于流转判定(时间戳 + 通知)
|
||||
_wo_old_status = getattr(obj, 'Status', None) if table_name == 'WorkOrder' else None
|
||||
pascal_map = self._BuildFieldMap(model)
|
||||
# UserRoleWorkgroup:表单用「部门员工关联」(UserRoleDepartmentUUID)选择器,
|
||||
# 编辑时改绑人员 → 解析出 UserRoleUUID 覆盖提交值(关系表只存用户-角色对,部门不落库)
|
||||
if table_name == 'UserRoleWorkgroup' and (
|
||||
# UserRoleWorkgroup / UserRoleDepartment:表单用「部门员工关联」(UserRoleDepartmentUUID)
|
||||
# 选择器,编辑时改绑人员 → 解析出 UserRoleUUID 覆盖提交值(关系表只存用户-角色对,
|
||||
# 部门不落库;UserRoleDepartment 的部门保持原值/表单上下文值,不随改绑人员漂移)
|
||||
if table_name in ('UserRoleWorkgroup', 'UserRoleDepartment') and (
|
||||
data.get('UserRoleDepartmentUUID') or data.get('UserRoleDepartmentUUID_id')):
|
||||
_urw_de, _urw_de_err = self._ResolveDeptEmployee(data)
|
||||
if _urw_de_err:
|
||||
return {'error': _urw_de_err, 'field': 'UserRoleDepartmentUUID'}
|
||||
if _urw_de and _urw_de.get('UserRoleUUID'):
|
||||
data = {**data, 'UserRoleUUID': _urw_de['UserRoleUUID']}
|
||||
if table_name == 'UserRoleDepartment' and not (
|
||||
data.get('DeptUUID') or data.get('DeptUUID_id')):
|
||||
data = {**data, 'DeptUUID': _urw_de['DeptUUID']}
|
||||
# EmployeeProfile(员工档案)直接绑「部门-员工」关系:解析选择器提交的
|
||||
# UserRoleDepartmentUUID → UserUUID/DeptUUID/DeptName(未提交/解析不出则保持原值)
|
||||
if table_name == 'EmployeeProfile':
|
||||
|
||||
Reference in new issue
Block a user