diff --git a/apps/api/src/isms/dto/export-isms-document.dto.ts b/apps/api/src/isms/dto/export-isms-document.dto.ts
index 632b1a292..b91f83c87 100644
--- a/apps/api/src/isms/dto/export-isms-document.dto.ts
+++ b/apps/api/src/isms/dto/export-isms-document.dto.ts
@@ -12,7 +12,8 @@ export class ExportIsmsDocumentDto {
@ApiPropertyOptional({
description:
- 'Published version to export. Omit to export the current working draft; ' +
+ "Published version to export. Omit to export the document's current " +
+ 'published version (or the working draft if it has never been published); ' +
'provide a version id to download exactly what was approved at that version.',
})
@IsOptional()
diff --git a/apps/api/src/isms/isms.service.spec.ts b/apps/api/src/isms/isms.service.spec.ts
index af4c4e5d0..7d801200f 100644
--- a/apps/api/src/isms/isms.service.spec.ts
+++ b/apps/api/src/isms/isms.service.spec.ts
@@ -89,6 +89,29 @@ describe('IsmsService ensureSetup', () => {
expect(mockDb.ismsDocument.findMany).toHaveBeenCalledTimes(1);
expect(result.documents).toHaveLength(1);
});
+
+ it('reports hasApprovedVersion from a published version OR an approved status', async () => {
+ (mockDb.frameworkEditorFramework.findUnique as jest.Mock).mockResolvedValue({
+ id: 'fw_1',
+ requirements: [],
+ });
+ (mockDb.ismsDocument.findMany as jest.Mock).mockResolvedValueOnce([
+ // Published version exists.
+ { id: 'd1', type: 'isms_scope', status: 'draft', requirementId: null, currentVersionId: 'isms_ver_1' },
+ // Approved before versioning existed: no version row, but still approved.
+ { id: 'd2', type: 'leadership_commitment', status: 'approved', requirementId: null, currentVersionId: null },
+ // Never approved.
+ { id: 'd3', type: 'objectives_plan', status: 'draft', requirementId: null, currentVersionId: null },
+ ]);
+
+ const result = await service.ensureSetup({ ...dto, canWrite: false });
+
+ expect(result.documents.map((d) => d.hasApprovedVersion)).toEqual([
+ true,
+ true,
+ false,
+ ]);
+ });
});
describe('template-driven (templates seeded)', () => {
diff --git a/apps/api/src/isms/isms.service.ts b/apps/api/src/isms/isms.service.ts
index 15dbc1974..e0a73497f 100644
--- a/apps/api/src/isms/isms.service.ts
+++ b/apps/api/src/isms/isms.service.ts
@@ -65,9 +65,13 @@ export class IsmsService {
type: doc.type,
status: doc.status,
requirementId: doc.requirementId,
- // A published version exists (the document has been approved at least
- // once) — independent of whether the draft has since been edited.
- hasApprovedVersion: doc.currentVersionId != null,
+ // The document has an approved artifact: either a published version row
+ // (approved under the versioning model) or `status === 'approved'` — the
+ // latter covers documents approved before versioning existed, whose legacy
+ // version rows were dropped by the migration and which capture a real
+ // versioned artifact on their next approval.
+ hasApprovedVersion:
+ doc.currentVersionId != null || doc.status === 'approved',
})),
};
}
diff --git a/apps/app/src/app/(app)/[orgId]/documents/isms/components/IsmsApprovalSection.tsx b/apps/app/src/app/(app)/[orgId]/documents/isms/components/IsmsApprovalSection.tsx
index d5bc90fd5..37ba6d724 100644
--- a/apps/app/src/app/(app)/[orgId]/documents/isms/components/IsmsApprovalSection.tsx
+++ b/apps/app/src/app/(app)/[orgId]/documents/isms/components/IsmsApprovalSection.tsx
@@ -128,8 +128,8 @@ export function IsmsApprovalSection({
v{publishedVersion}
{' '}
- stays live and exportable. Your changes are an in-progress draft (v
- {nextDraftVersion}) — submit it for approval to publish.
+ stays live and exportable. Your changes are an in-progress draft{' '}
+ {`(v${nextDraftVersion})`} — submit it for approval to publish.
)}
diff --git a/apps/app/src/app/(app)/[orgId]/documents/isms/components/ScopeClient.tsx b/apps/app/src/app/(app)/[orgId]/documents/isms/components/ScopeClient.tsx
index f3b006621..391391dd1 100644
--- a/apps/app/src/app/(app)/[orgId]/documents/isms/components/ScopeClient.tsx
+++ b/apps/app/src/app/(app)/[orgId]/documents/isms/components/ScopeClient.tsx
@@ -64,10 +64,17 @@ export function ScopeClient(props: ScopeClientProps) {
};
const narrative = toScopeNarrative(document.draftNarrative);
+ // Re-seed the form (via remount) whenever the persisted draft content
+ // changes — e.g. after "Generate from platform data" or a reload — not
+ // just when the published version changes. ScopeForm reads its defaults
+ // at mount, so the key must reflect the draft, not currentVersionId alone.
+ const formKey = `${document.currentVersionId ?? 'draft'}:${JSON.stringify(
+ document.draftNarrative ?? {},
+ )}`;
return (