Appearance
Client Groups
Business Context
Business-Level Definition
The platform operator organises the clients it manages into named groups — regional groupings such as "Plzeňský kraj", or ad-hoc groupings such as a shared account manager's portfolio. A group is a label the operator attaches across clients, not a way clients own or manage one another.
Requirements Definition
Two things need this grouping. First, reporting: the operator wants to see and act on a set of clients together rather than one at a time. Second, access control: a platform-level role can be restricted to acting only on clients within one or more named groups, rather than being granted every client or none — see Users & Access for how a restriction is enforced once granted; this feature only defines the groups a restriction can name.
Every group states how many clients it may hold. The operator sets that number when the group is created, and it is enforced on every attempt to add a client — a group cannot silently grow past what the operator decided it should hold.
Technical Context
User Stories / Use Cases
- As the platform operator, I want to create a named client group with a maximum size, so I can organise clients for reporting before I start adding any.
- As the platform operator, I want to see every client group and how full each one is, so I know at a glance which groups still have room.
- As the platform operator, I want to add a client to a group, and to be stopped if the group is already full, so a group never grows past the size I decided.
- As the platform operator, I want to remove a client from a group without affecting the client itself or any other group it belongs to.
- As the platform operator, I want to rename a group or change its capacity, so a group set up too small can grow, or a stale name can be corrected.
- As the platform operator, I want to be stopped from deleting a group that still has members, so I don't lose the record of who was in it by mistake.
- As the platform operator, I want a client group to be nameable and pickable when I restrict an Admin-manažer's access, so the two features share the same set of groups.
UI/UX Design
A dedicated "Manage groups" screen on the platform administration application, separate from the client Detail tab (see Client Management) — a client's group memberships are managed here, not there.
| State | What is shown |
|---|---|
| Group list | Every group with its name, capacity, and current member count (e.g. "8 / 25") |
| Creating a group | Name and capacity (a required positive integer); no members yet |
| Group detail | The group's members, each with an option to remove; an "+Add client" control that searches the platform client catalogue |
| Group at capacity | The "+Add client" control is disabled, with a note that the group is full; capacity can be raised from the group's own edit action |
| Renaming or resizing a group | Same form as creation, pre-filled; lowering capacity below the current member count is refused with the reason |
| Deleting a group | Offered only when the group has no members; a group with members shows why deletion is unavailable instead of a confirmation dialog |
| Group with no members | Shown normally in the list, deletable at any time |
Interactive wireframes covering these states: design/wireframes.html.
Functional Requirements
- Create a client group with a name and a required, positive integer capacity — no group is created "unlimited".
- List every client group with its name, capacity and current member count.
- Rename a group and/or change its capacity; reject a capacity lower than the group's current member count.
- Delete a group only when it has no active members; otherwise refuse and name the member count.
- Add a client to a group; reject if the client is already an active member of that group, or if the group is already at capacity.
- Remove a client from a group; this ends only that one membership, leaving the client's other group memberships and the client record itself untouched.
- A client may belong to any number of groups at once, each independently capacity-checked.
- Record an entry in the platform audit trail for every group created, renamed, resized, deleted, and every membership added or removed — the event, and the entity (group or membership) it is filed against.
Internationalization & Localization
N/A — group names and screen labels are free text / the platform's own labelling mechanism (GUI Labelling); nothing here is a translated enumerable value.
Non-Functional Requirements
N/A — no reliability, availability or scalability concern beyond what Performance and Transactional Operations already cover.
Performance
The catalogue and any one group's membership are both small (tens of groups, tens of clients per group at most), read on screen load. No index beyond the unique membership key is needed, and no measurement target is set.
Transactional Operations
Creating, renaming and resizing a group is a single-row write. Deleting a group reads the active member count and soft-deletes the group in the same transaction, so a concurrent membership add cannot race a delete into an inconsistent state. Adding a member reads the group's capacity and current member count, checks for a duplicate, and inserts — all in the same transaction, so two concurrent adds cannot both slip past a group's last free slot. Removing a member is a single-row soft-delete.
Processes & Related Systems / Components
| Trigger | Owner of the trigger | What this feature does |
|---|---|---|
| Operator restricts an Admin-manažer's access to specific clients | Users & Access | Supplies the set of client groups a restriction can name — see Restriction collection |
| Operator organises clients for reporting | this feature | Groups and memberships are created, listed and maintained independently of any restriction use |
Diagrams & Models
Adding a client to a group:
sequenceDiagram
actor A as Platform operator
participant S as Client Groups
participant G as clientGroup
participant M as clientGroupMembership
A->>S: Add client X to group Y
S->>G: Read capacity + current member count
S->>M: Check X not already an active member
alt group full or already a member
S-->>A: 409 (ERR_CLIENT_GROUP_FULL / ERR_CLIENT_ALREADY_IN_GROUP)
else room available
S->>M: Insert membership row
M-->>S: Stored
S-->>A: 201 Created
end
API Analysis
See api-a.md.
Domain Model (ER diagram) & Data Attribute Table
erDiagram
CLIENT ||--o{ CLIENT_GROUP_MEMBERSHIP : "belongs to"
CLIENT_GROUP ||--o{ CLIENT_GROUP_MEMBERSHIP : "holds"
- clientGroup — created by this feature; a named, capacity-bounded grouping of clients, platform-scoped.
- clientGroupMembership — created by this feature; the many-to-many link between a client and a client group.
- client — the platform account entity this feature groups; not modified by this feature beyond removing its retired
clientGroupIdcolumn (see that page).
Data
No seed data — the catalogue starts empty and the operator creates groups as needed.
Test Data
Use the project's Test Data location named in the overlay. Search it for client_group / client_group_membership — the cases worth having are a group at exactly its capacity (to exercise the full-group rejection) and a client that belongs to more than one group.
Logging
The project has no shared logging-conventions document yet, so this feature's events are stated in full here.
.info— group created, renamed, resized or deleted: group id, actor,requestId..info— client added to or removed from a group: group id, client id, actor,requestId..warn— membership add rejected (group full or duplicate): group id, client id, reason.
Monitoring
N/A — the project's monitoring convention is not yet defined org-wide (open item, not a decision this feature can make); no feature-level need beyond what logging already covers.
Caching
None — the catalogue is small and read on screen load; nothing here benefits from a cache.
Backward Compatibility and Migration
EM2 held a hierarchical client_groups table (a group could itself act as a parent with member clients under it); the code repository's client ↔ tenant architecture decision retired that hierarchy in favour of client → tenant ownership (docs/features/em3-44-clients/decision-client-tenant-relationship.md, code repo origin/main, 968e59e: "clientGroup zaniká"). This feature is not a revival of that hierarchy — it is a flat, non-hierarchical tag: a client group here never owns, delegates to, or licenses another client, and is never consulted for entitlement. EM2's example groups (a regional grouping such as "Plzeňský kraj", with real 1:N membership counts in its data) map directly onto this feature's clientGroup + clientGroupMembership, without the parent-child structure EM2 also carried.
Legal Context
No licence, contract or regulation governs how the platform operator labels its own clients internally, so this chapter is otherwise N/A. One point is worth stating plainly rather than leaving implicit: a client's group membership carries no contractual weight of its own — its licence terms, module entitlements and any other contractual obligation are governed entirely by client and are unaffected by which groups, if any, it belongs to, or by a group being created, resized, or deleted.
Cybersecurity Considerations
Writing (create, rename, resize, delete a group; add or remove a member) is gated by platform.client-groups.write, distinct from the read permission, on the platform administration surface only — no tenant-scoped surface can read or write this catalogue. Every change is recorded in the platform audit trail.
Data Privacy. No personal data beyond the client name/IČO already held on client and the actor metadata every record in the system carries.
Risk Assessment
Business Risks
A group is a reporting and access-scoping convenience; it holds no entitlement, so a wrong or missing membership does not expose or block a client's licence, modules or billing. The mitigation already built in is the capacity cap and the deletion guard: a group cannot silently overgrow, and deleting one cannot silently orphan its membership history without the operator seeing why.
Technical Risks
| Risk | Consequence | Mitigation |
|---|---|---|
| Two operators add the same client to a group's last free slot at once | One add should be rejected, not both accepted past capacity | Capacity check and insert happen inside one transaction |
| A group is deleted while a membership add is in flight | An orphaned membership row pointing at a deleted group | Member-count check and soft-delete happen inside one transaction |
| A client-group restriction (Users & Access) names a group that is later deleted | A restriction referencing a group that no longer exists | Out of scope here — see Users & Access for how a restriction handles a retired group |
| Cascade — this feature is unavailable | Reporting groupings and new access restrictions by group can't be changed; existing restrictions keep working from their last-known membership | No fallback needed — this is an administrative convenience, not a hot path |
Auditing, Reporting & Measurement
Every group create, rename, resize and delete, and every membership add and remove, is filed in the platform audit trail against the entity it changed, with the actor and the time; the audited field sets are on the entities' own pages. The feature reports nothing else — reporting on grouped clients is done by whatever consumes the group (e.g. a filtered client report), not by this feature itself.