Teams & Templates
Teams, membership, estimate scales, task templates, and custom-field plumbing in the Operation plugin.
Backend code lives under backend/plugins/operation_api/src/modules/{team,template,fields}/. Teams are the scoping unit: statuses, cycles, triage queues, and task estimate scales all hang off a team.
Data models
operation_teams:{ icon, name, description, memberIds: [String], estimateType: Int, cycleEnabled: Boolean, triageEnabled: Boolean }.operation_team_members:{ memberId, teamId, role };rolesupportsadmin/member/leadin the model but is deprecated on the GraphQL type.operation_templates:{ name, defaults: JSON, teamId, createdBy }; per-team task templates whosedefaultsprefill a new task.
Creating a team (teamAdd → Team.createTeam) inserts TeamMember rows for the creator and memberIds, then seeds the five default statuses (backlog, todo, in progress, done, cancelled); see Tasks & Statuses.
Estimate scales
estimateType is one of TeamEstimateTypes (team/utils.ts): NOT_IN_USE=1, DEFAULT=2 (0–5), FIBONACCI=3 (0,1,2,3,5,8), EXPONENTIAL=4 (0,1,2,4,8,16). getTeamEstimateChoises(teamId) (sic; the operation name is misspelled in the schema) returns the { value, label } list for the team's type, or null when estimates are off.
Switching estimateType wipes estimates
updateTeam sets estimatePoint: null on every task of the team when estimateType changes to NOT_IN_USE. Other scale switches keep existing values even if they are not in the new scale.
Toggling cycleEnabled on SaaS (VERSION=saas) also updates the organization's cycleEnabled flag, which is what the daily cycle worker filters on; see Projects, Cycles & Milestones.
Queries and mutations
| Operation | Kind | Permission |
|---|---|---|
getTeam(_id) / getTeams(name, userId, teamIds, projectId, isTriageEnabled, teamId, orderBy) | query | teamRead |
getTeamMembers(teamId, teamIds) | query | teamRead |
getTeamEstimateChoises(teamId) | query | teamRead |
teamAdd(name, description, icon, memberIds) / teamUpdate(_id, …estimateType, cycleEnabled, triageEnabled) | mutation | teamCreate / teamUpdate |
teamRemove(_id) | mutation | teamRemove |
teamAddMembers(_id, memberIds) / teamRemoveMember(teamId, memberId) / teamUpdateMember(_id) | mutation | teamMemberManage |
operationTemplates(teamId) / operationTemplateDetail(_id) | query | login only; no checkPermission |
operationTemplateAdd(name, defaults, teamId) / operationTemplateEdit / operationTemplateRemove | mutation | login only; no checkPermission |
Custom fields
fields.getFieldList on the plugin tRPC router returns the filterable/inputable field list for moduleType: 'task' or 'project'. modules/fields/fieldUtils.ts builds it from the Mongoose schemas plus hand-declared options: priority renders No Priority/Minor/Medium/High/Critical (values 0–4), statusType maps to the STATUS_TYPES labels, and teamId, assigneeId, createdBy, tagIds get selection configs (e.g. queryName: 'users').
Custom property values are stored on the record under propertiesData keyed by Core field id; the plugin meta exposes task and project property types with systemFields code/name/type shown as "Basic information" in Settings → Properties. propertiesData is only written through task.createFromSource; the task GraphQL type does not expose it.
Permissions
Team module actions: teamRead (always granted), teamCreate, teamUpdate, teamRemove, teamMemberManage (membership changes). Template resolvers perform no checkPermission call, so they fall back to the platform default (any logged-in user). Scopes are own (creator), group (member's teams), all. A member who cannot see a team has lost the group scope; re-add them with teamAddMembers.