feat(documents): entity-folder archive / restore / demote helpers

applyEntityArchivedSuffix stamps " (archived)" + archived_at on the
entity subfolder so the UI mutes it and auto-deposit halts. Restore
is the inverse. demoteSystemFolderOnEntityDelete flips
system_managed=false, appends " (deleted)", and clears the entity FK
so the partial unique index releases the slot — orphaned files
retain their entity FK snapshots and surface in the rep's clean-up
view.

All three helpers are best-effort from the entity-side hooks; folder
errors are logged at warn level but do not fail the entity-update
operation. UPDATE WHERE clauses include port_id (defense-in-depth).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-05-11 11:34:02 +02:00
parent 3b34b41989
commit 4c5dc7ec17
5 changed files with 211 additions and 3 deletions

View File

@@ -6,7 +6,10 @@ import { companies } from '@/lib/db/schema/companies';
import { createAuditLog, type AuditMeta } from '@/lib/audit';
import { NotFoundError, ValidationError } from '@/lib/errors';
import { logger } from '@/lib/logger';
import { syncEntityFolderName } from '@/lib/services/document-folders.service';
import {
syncEntityFolderName,
applyEntityArchivedSuffix,
} from '@/lib/services/document-folders.service';
import { emitToRoom } from '@/lib/socket/server';
import { setEntityTags } from '@/lib/services/entity-tags.helper';
import { diffEntity } from '@/lib/entity-diff';
@@ -190,6 +193,10 @@ export async function archiveYacht(id: string, portId: string, meta: AuditMeta)
.set({ archivedAt: new Date() })
.where(and(eq(yachts.id, id), eq(yachts.portId, portId)));
void applyEntityArchivedSuffix(portId, 'yacht', id).catch((err) => {
logger.warn({ err, yachtId: id }, 'Failed to apply archived suffix to yacht folder');
});
void createAuditLog({
userId: meta.userId,
portId,