meraproject/openspec/specs/service-project-members/spec.md

109 lines
4.5 KiB
Markdown
Raw Normal View History

# service-project-members
## Purpose
Service-to-service API для upsert участника проекта Merakomis без impersonation сотрудника (только API-ключ).
## Requirements
### Requirement: Service upsert project team member
The system SHALL expose `PUT /api/service/project-members` that creates or updates a row in `tMerakomisTeamMember` for the given `project_id` and `emp_id`.
The endpoint MUST resolve `team` from `tMerakomisProject.team` for `project_id`.
If a row with the same `(team, emp)` already exists, the system MUST UPDATE it; otherwise MUST INSERT.
The request MUST require `X-Api-Key` (or `Authorization: Bearer`) when the server API key is configured.
The request MUST NOT require `X-Acting-Emp-Id`.
The system MUST NOT apply portal `Rules::isRwMember` / `can_write_project_member` checks for this endpoint.
Required JSON fields: `project_id`, `emp_id`, `section_id`, `role`.
Optional JSON fields: `active` (default `true`), `text` (default empty string).
The response MUST include `ok: true` and a member object with the same fields as one item of `GET /api/project-members` (`member_id`, `emp_id`, `emp_name`, `project_id`, `project_code`, `project_name`, `step_name`, `status`, `status_name`, `archive`, `archive_date`, `section_id`, `section_name`, `role`, `active`).
#### Scenario: Add new member with API key only
- **WHEN** a valid API key is provided
- **AND** `X-Acting-Emp-Id` is omitted
- **AND** no `tMerakomisTeamMember` row exists for `(team, emp)`
- **AND** `section_id` is allowed for `project_id`
- **AND** `emp_id` refers to a non-archived, non-removed employee
- **AND** `role` is a valid member role
- **THEN** system inserts a new TeamMember row
- **AND** returns `200` with `ok: true` and the new member object including `member_id`
#### Scenario: Update existing member with API key only
- **WHEN** a TeamMember row already exists for `(team, emp)`
- **AND** a valid API key is provided without acting header
- **THEN** system updates `section`, `role`, `active`, and `text`
- **AND** returns `200` with updated member object (same `member_id`)
#### Scenario: Deactivate member via service endpoint
- **WHEN** client sends `PUT /api/service/project-members` with `active: false`
- **AND** a valid API key is provided
- **THEN** system sets `active` to 0
- **AND** member no longer appears in `GET /api/project-members?active_only=true`
#### Scenario: Missing API key rejected
- **WHEN** server has `USER_READER_API_KEY` configured
- **AND** client calls `PUT /api/service/project-members` without a valid key
- **THEN** system returns `401 Unauthorized`
### Requirement: Service endpoint reuses portal write validation
The system MUST apply the same business validation as `PUT /api/project-members` for project existence, employee existence and eligibility, section membership in the project, role validity, and presence of a project team.
#### Scenario: Invalid section for project
- **WHEN** `section_id` is not in `tMerakomisProjectSection` for `project_id`
- **THEN** system returns `400` with a structured error indicating invalid section
#### Scenario: Project has no configured sections
- **WHEN** `tMerakomisProjectSection` has no rows for `project_id`
- **AND** client attempts to add a member via the service endpoint
- **THEN** system returns `400` indicating sections must be configured on the project first
#### Scenario: Archived employee rejected
- **WHEN** `emp_id` refers to an employee with `archive=1` or `removed=1`
- **THEN** system returns `400` with validation error for employee
#### Scenario: Unknown employee
- **WHEN** `emp_id` does not exist in the employee table
- **THEN** system returns `404`
#### Scenario: Unknown project
- **WHEN** `project_id` does not exist
- **THEN** system returns `404`
#### Scenario: Invalid role
- **WHEN** `role` is 0, omitted, or not a valid member role
- **THEN** system returns `400` with validation error for role
### Requirement: Portal project-members write remains unchanged
The system MUST continue to require `X-Acting-Emp-Id` and `isRwMember` authorization for `PUT /api/project-members`.
#### Scenario: Portal PUT still requires acting
- **WHEN** client calls `PUT /api/project-members` without `X-Acting-Emp-Id`
- **THEN** system returns an error indicating missing acting identity (same behavior as before this change)
#### Scenario: Portal PUT still enforces permissions
- **WHEN** acting employee does not meet `isRwMember` conditions
- **AND** client calls `PUT /api/project-members`
- **THEN** system returns `403` with structured error `forbidden`