Files
pn-new-crm/src/lib/db/migrations/0078_files_interest_id.sql

31 lines
1.2 KiB
MySQL
Raw Normal View History

feat(documents): foundation for nested interest subfolders (phase 1/3) Sets up the schema + service primitives the rest of the nested- document-subfolders feature will build on (master UAT line 728+). This commit is INFRASTRUCTURE ONLY — the upload-zone scope radio, lifecycle hooks for outcome rename, aggregated-projection list query, and backfill script are deferred to follow-up commits. Schema (migration 0078_files_interest_id.sql): - `files.interest_id` text REFERENCES interests(id) ON DELETE SET NULL. Mirrors the existing documents.interest_id; lets file uploads be scoped to a deal while still rolling up to the parent client folder. - idx_files_interest + idx_files_port_interest for the aggregated- projection queries that will surface "This deal" vs "From client" file lists. Service: - EntityType extended to include 'interest'. Interest folders parent under the owning client's entity folder (not at a system root), so the tree reads Clients/Acme/Deal A1-A3/ — nested. - ensureEntityFolder recursively ensures the parent client folder first when given an interest, guaranteeing the deal folder lands inside the right client subfolder even when the first artifact on the deal predates any client-level upload. - resolveEntityDisplayName for interest: "Deal — <mooringNumber>" (when a primary berth is linked) or "Deal <YYYY-MM-DD>" as the stable fallback. Dynamic-import on getPrimaryBerth dodges the circular dep between document-folders.service and interest-berths.service. Aggregated projection (files.ts): - listFilesAggregatedByEntity SELECT now includes the new interest_id column so AggregatedFileRow's structural type matches. Downstream consumers gain access to the deal scope; the actual "From this deal" subheading in InterestDocumentsTab is wired in the follow-up. Remaining work (tracked in master UAT line 728+, parked for next session): - UploadZone `scopeOptions` radio (single-option pickers hide the radio entirely for client/yacht/company surfaces). - Lifecycle hooks for interest outcome → folder rename ("Deal A1-A3 (Won)") via soft-rescue per CLAUDE.md. - listFilesAggregatedByEntity rewrite to surface "This deal" vs "From client" subheadings on InterestDocumentsTab. - Documents Hub tree rendering for nested interest folders. - backfill script: existing files with entity_type='interest' + entity_id but missing interest_id column → populate. Verified: tsc clean, vitest 1448/1448 after dev-DB migration applied. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-21 20:18:40 +02:00
-- Phase 1 of the nested document subfolders feature (master UAT line 728+):
--
-- 1. Add a nullable `interest_id` column to `files` so uploads scoped
-- to a deal can be filed under the interest subfolder while still
-- rolling up to the parent client folder. Mirrors the existing
-- `documents.interest_id` semantics: scoping FK that holds a
-- "from-interest" attribution even when the parent client/yacht/
-- company shifts.
--
-- 2. Index on `(port_id, interest_id)` for the aggregated-projection
-- queries that will surface "this-deal" files vs "from-client" files
-- in InterestDocumentsTab.
--
-- 3. Soft FK (`ON DELETE SET NULL`) so a hard-deleted interest doesn't
-- orphan the file — the audit trail stays intact and the file remains
-- findable under the parent client folder.
--
-- Apply in dev:
-- PGPASSWORD=changeme psql -h localhost -p 5434 -U crm -d port_nimara_crm \
-- -f src/lib/db/migrations/0078_files_interest_id.sql
ALTER TABLE files
ADD COLUMN IF NOT EXISTS interest_id text
REFERENCES interests(id) ON DELETE SET NULL;
CREATE INDEX IF NOT EXISTS idx_files_interest
ON files (interest_id);
CREATE INDEX IF NOT EXISTS idx_files_port_interest
ON files (port_id, interest_id);