Every document in QualityDocD follows a structured lifecycle designed to enforce quality controls before content becomes official. From the moment an author saves a first draft to the point a document is superseded by a newer version, the system tracks every state change, records it in both the SQL Server audit log and the PostgreSQLDocumentation Index
Fetch the complete documentation index at: https://mintlify.com/juescoryisus/QualityDocD/llms.txt
Use this file to discover all available pages before exploring further.
audit_entries table, and syncs metadata to MongoDB. Understanding the lifecycle means understanding who can act at each stage — and what gates must pass before a document reaches the hands of the people who rely on it.
The Seven States
TheDocumentStatus enum in Document.cs defines exactly seven states. Each state has a precise meaning and a limited set of valid successors.
Draft
Initial draft. The document has been created but not yet submitted for review. Authors can freely edit content, replace file attachments, and update metadata.
CanSubmitForReview() returns true in this state.UnderReview
The author submitted the document and assigned one or more reviewers. Each reviewer holds a
DocumentApproval record in Pending status. The document is locked for editing until a decision is made.PendingChanges
At least one reviewer called
RequestChanges. The document is returned to the author for revision. All previous DocumentApproval records are cleared when the author resubmits. CanSubmitForReview() returns true in this state.UnderSecondReview
The author applied the requested changes and resubmitted. A fresh set of
DocumentApproval records is created. IsInReview() returns true for both UnderReview and UnderSecondReview.Approved
Every assigned reviewer has set their
ApprovalStatus to Approved. The document’s ApprovedAt timestamp is stamped. The document is now the authoritative version. IsFinal() returns true.Rejected
A reviewer definitively rejected the document. The
RejectedAt timestamp is stamped and IsFinal() returns true. A rejection is permanent — a new document version must be created to continue.Obsolete
An Admin or Manager has superseded the document by marking it obsolete, typically after a newer version has been approved.
IsFinal() returns true. This state can also be reached directly from Approved.State Summary Table
| Status | CanSubmitForReview() | IsInReview() | IsFinal() |
|---|---|---|---|
Draft | ✅ true | ❌ false | ❌ false |
UnderReview | ❌ false | ✅ true | ❌ false |
PendingChanges | ✅ true | ❌ false | ❌ false |
UnderSecondReview | ❌ false | ✅ true | ❌ false |
Approved | ❌ false | ❌ false | ✅ true |
Rejected | ❌ false | ❌ false | ✅ true |
Obsolete | ❌ false | ❌ false | ✅ true |
State Transitions
The following table lists every valid transition in the system, the action that triggers it, and the role required to perform that action.| From | To | Trigger | Who Can Trigger |
|---|---|---|---|
Draft | UnderReview | Author submits for review with at least one reviewer assigned | Any authenticated user (document owner) |
UnderReview | Approved | All assigned reviewers approve | Admin, Manager, Reviewer |
UnderReview | Rejected | Any reviewer rejects (with mandatory comment) | Admin, Manager, Reviewer |
UnderReview | PendingChanges | Any reviewer requests changes (with mandatory comment) | Admin, Manager, Reviewer |
UnderSecondReview | Approved | All assigned reviewers approve | Admin, Manager, Reviewer |
UnderSecondReview | Rejected | Any reviewer rejects (with mandatory comment) | Admin, Manager, Reviewer |
UnderSecondReview | PendingChanges | Any reviewer requests changes (with mandatory comment) | Admin, Manager, Reviewer |
PendingChanges | UnderSecondReview | Author resubmits after applying changes | Any authenticated user (document owner) |
Approved | Obsolete | Admin or Manager marks the document obsolete | Admin, Manager |
Rejected | Obsolete | Admin or Manager marks the document obsolete | Admin, Manager |
Final states —
Approved, Rejected, and Obsolete — cannot transition back to any earlier state. IsFinal() returning true means the document’s lifecycle is closed. To continue work on a rejected or obsolete document, create a new document version with incremented Version number.Domain Helper Methods
TheDocument class exposes three boolean helpers that the service layer and controller use as guards before executing any state-changing operation.
CanSubmitForReview() — Draft or PendingChanges
CanSubmitForReview() — Draft or PendingChanges
DocumentService.SubmitForReviewAsync() before creating DocumentApproval records. If the document is in any other state the service returns an error and no database changes are made.IsInReview() — UnderReview or UnderSecondReview
IsInReview() — UnderReview or UnderSecondReview
ApproveAsync(), RejectAsync(), and RequestChangesAsync(). Reviewer actions are only valid while the document is in an active review state.IsFinal() — Approved, Rejected, or Obsolete
IsFinal() — Approved, Rejected, or Obsolete
Edit controller action returns an error if Status is not "Draft" or "PendingChanges", which aligns with the inverse of this helper.Per-Reviewer Approval Status
Each reviewer’s decision is stored in a separateDocumentApproval row. The ApprovalStatus enum tracks the individual verdict independently from the document-level DocumentStatus.
ApprovalStatus | Meaning |
|---|---|
Pending | The reviewer has been assigned but has not yet acted. This is the default when a DocumentApproval record is created. |
Approved | The reviewer approved the document without observations. When all reviewers in a round reach Approved, the document moves to DocumentStatus.Approved. |
Rejected | The reviewer definitively rejected the document. The document moves immediately to DocumentStatus.Rejected regardless of other reviewers’ pending decisions. |
RequestChanges | The reviewer requires revisions before approving. The document moves to DocumentStatus.PendingChanges. A non-empty Comments field is mandatory. |
Multi-Reviewer Approval Logic
When a document has multiple reviewers,ApproveAsync() checks whether every other approval record has already reached ApprovalStatus.Approved before promoting the document:
DocumentApproval is saved as Approved but the document status remains UnderReview or UnderSecondReview until the last reviewer acts.
The PendingChanges Loop
The change-request cycle is a first-class feature of the lifecycle, allowing iterative improvement without creating a new document version.Reviewer calls RequestChanges
A reviewer with role Admin, Manager, or Reviewer submits the
RequestChanges action with a mandatory comment. DocumentService.RequestChangesAsync() sets the reviewer’s ApprovalStatus to RequestChanges, then sets DocumentStatus to PendingChanges.Author edits the document
The document is unlocked for editing (
CanSubmitForReview() returns true). The author updates the content and optionally replaces the file attachment (which increments Version).Author resubmits
The author calls
SubmitForReview again, assigning reviewers for the second round. Because doc.Status == DocumentStatus.PendingChanges, SubmitForReviewAsync() clears all previous DocumentApproval records and creates fresh ones. The document advances to UnderSecondReview.Allowed File Attachments
DocumentService enforces the following file extensions via the AllowedExtensions set. Attempting to upload a file with any other extension returns a validation error before any database record is created or updated.
- Office & PDF
- Images
- CAD & Engineering
- Data & Archives
| Extension | Type |
|---|---|
.pdf | Portable Document Format |
.docx / .doc | Microsoft Word |
.xlsx / .xls | Microsoft Excel |
.pptx / .ppt | Microsoft PowerPoint |