---
title: NoteService
summary: API reference for NoteService (SAIF.Services.Groups.Notes).
status: draft
owner: SAIF Doc Gen
last_reviewed: 2026-09-24
library: SAIF.Services
reference_type: package
---

# NoteService

Implements `INoteService` for group notepad entries, coordinating retrieval, filtering, adding, appending, editing, and deleting notes attached to groups (`GroupNote`/`Notepad`). It depends on `IUnitOfWork` for repository access and transactions, `IValidationService` for FluentValidation-based edit validation (via `NoteEditValidator`), `IUserProfileService` for current-user identity and role checks, and `IWorkCalendarService` to compute the "two work days ago" cutoff that gates edit/delete/append permissions. Its static constructor registers AutoMapper maps for `GroupNote`/`Notepad`→`Note` and reuses `GroupsMappingHelper.CreateGroupMap()`.

## Purpose
- Retrieves a single note or all notes for a group (including notes on its parent group), filtering out confidential notes unless the current user is authorized to see them.
- Filters an in-memory note collection by category, type, creator/updater, and date range (`NoteFilterCriteria`).
- Adds a new note, enforcing edit authorization and validation, and stamping create/update audit fields.
- Appends text to an existing note (only allowed once the note is older than two work days) or edits a note's text directly (only allowed within two work days of creation), enforcing optimistic-concurrency checks against `UpdateDate`.
- Deletes a note, allowing unrestricted deletion for super-delete users and time-limited deletion (within two work days) for regular edit users.
- Reports whether the current user can add notes or see confidential notes, and returns the available note categories.

## Context
- Namespace: `SAIF.Services.Groups.Notes`
- Assembly/package: `SAIF.Services` (packaged inside the `SAIF.SAIFCode.Core` NuGet package)

## Public members
| Member | Signature | Purpose |
| --- | --- | --- |
| NoteService(...) | `public NoteService(IUnitOfWork unitOfWork, IValidationService validationService, IUserProfileService userProfileService, IWorkCalendarService workCalendarService)` | Validates all four collaborators are non-null and stores them for use by every other member. |
| GetNote | `public Note GetNote(long noteId)` | Returns the mapped `Note` for `noteId`, or `null` if not found or the caller lacks confidential-note visibility. |
| GetNotes(groupAcronym, criteria) | `public IEnumerable<Note> GetNotes(string groupAcronym, NoteFilterCriteria criteria)` | Returns the group's notes (own and parent-group notes) narrowed by `criteria`. |
| GetNotes(groupAcronym) | `public IEnumerable<Note> GetNotes(string groupAcronym)` | Returns all visible notes for the group and its parent group, mapped to `Note` contracts. |
| Filter | `public IEnumerable<Note> Filter(IEnumerable<Note> notes, NoteFilterCriteria criteria)` | Narrows an in-memory note collection by category code, type code, create/update user substring, and creation-date range, skipping any criterion set to its "all" sentinel value. |
| AddNote | `public long? AddNote(NoteEdit noteEdit, IMessageContainer messages)` | Adds a new note for the current user if authorized and valid, committing within a transaction; returns the new note ID, or `null` (with messages populated) on authorization/validation failure. |
| AppendNote | `public bool AppendNote(long noteId, NoteEdit noteEdit, IMessageContainer messages)` | Appends new text (with an "Added <date> by <user>" prefix) to an existing note if the caller is authorized, the note is older than two work days, there is no intervening update, and the appended text validates; returns whether the append succeeded. |
| AppendPrefixTextLength | `public int AppendPrefixTextLength { get; }` | Returns the character length of the "Added <date> by <user>" prefix that will be inserted when appending. |
| DeleteNote | `public bool DeleteNote(long noteId, IMessageContainer messages)` | Deletes the note if found, and if the caller is a super-delete user or the note was created within the last two work days; returns whether the delete succeeded. |
| EditNote | `public bool EditNote(long noteId, NoteEdit noteEdit, IMessageContainer messages)` | Edits an existing note's group, confidentiality, type, and text (with an "Updated <date> by <user>" suffix) if the caller is authorized, there is no intervening update, the edit window (two work days since creation) has not passed when text changed, and validation succeeds; returns whether the edit succeeded. |
| EditSuffixTextLength | `public int EditSuffixTextLength { get; }` | Returns the character length of the "Updated <date> by <user>" suffix appended when a note's text is edited. |
| GetCategories | `public IEnumerable<Category> GetCategories()` | Returns the note categories scoped to the group-note context (`NotepadContext.GroupNoteContextCode`). |
| DeleteExpiredNotes | `public bool DeleteExpiredNotes()` | Always throws `NotImplementedException`; this capability is not implemented for group notes. |
| CanUserSeeConfidentialNotes | `public bool CanUserSeeConfidentialNotes { get; }` | Returns whether the current user holds the confidential-notes role. |
| GetAppendPrefixTextLength | `public int GetAppendPrefixTextLength()` | Always throws `NotImplementedException`; superseded by the `AppendPrefixTextLength` property. |
| CanUserAddNote | `public bool CanUserAddNote { get; }` | Returns whether the current user is authorized to add notes (same check used to gate `AddNote`/`AppendNote`/`EditNote`). |

## Behavioral notes
- `AddNote`, `AppendNote`, `EditNote`, and `DeleteNote` each wrap their write in `unitOfWork.Begin()`/`Commit()`; none of them explicitly call `Rollback()` on failure paths, relying instead on returning early before `Commit()` is reached*.
- Edit authorization (`IsEditUser`) is cached per-instance in the `isUserAuthorizedToEditNotes` nullable field after the first check, so subsequent calls on the same service instance reuse the cached result rather than re-querying the user profile*.
- The optimistic-concurrency check compares `noteEdit.UpdateDate` to the stored `Note.UpdateDate` and rejects the write with "Error: Another user updated this note." on mismatch.
- `DeleteExpiredNotes` and `GetAppendPrefixTextLength` are unimplemented (`throw new NotImplementedException()`), so callers must not invoke them in current builds.

> **Note:** Statements marked with * were inferred from code structure, naming conventions, or
> contextual evidence where direct confirmation was not available in source artifacts. Statements
> marked with ** could not be confirmed; treat as unknown until verified.

## Sources
- Services/SAIF.Services/Groups/Notes/NoteService.cs
