diff --git a/src/lib/server/export/financial.ts b/src/lib/server/export/financial.ts index fbbab6d..2d03ca9 100644 --- a/src/lib/server/export/financial.ts +++ b/src/lib/server/export/financial.ts @@ -21,7 +21,9 @@ import { users, companyBankAccounts, companyCards, - companyAddresses + companyAddresses, + companyDocuments, + companyDocumentVersions } from '../db/schema.js'; import { csvBuild } from '$lib/utils/csv.js'; import { CARRIER_LABELS } from '../shipping/index.js'; @@ -74,6 +76,7 @@ export async function buildFinancialExport( ` company_bank_accounts.csv — company bank accounts`, ` company_cards.csv — company credit/debit cards (last 4 only)`, ` company_addresses.csv — legal/shipping/billing/other addresses`, + ` company_documents.csv — uploaded document metadata (files not bundled)`, ` projects.csv — all projects (active + inactive)`, ` parties.csv — all customers/suppliers (incl. archived; see deletedAt)`, ` employees.csv — all employees (incl. terminated/archived)`, @@ -210,6 +213,77 @@ export async function buildFinancialExport( zip.file('company_addresses.csv', withBom(csvBuild(rows))); } + // ── company_documents.csv ────────────────────────── + { + const docRows = await db + .select() + .from(companyDocuments) + .where(eq(companyDocuments.companyId, companyId)) + .orderBy(asc(companyDocuments.category), asc(companyDocuments.title)); + + // Latest version per document (joined) + const latestByDoc = new Map< + string, + { + versionNumber: number; + fileName: string; + mimeType: string; + sizeBytes: number; + uploadedBy: string | null; + uploadedAt: Date; + } + >(); + if (docRows.length > 0) { + const versionRows = await db + .select({ + documentId: companyDocumentVersions.documentId, + versionNumber: companyDocumentVersions.versionNumber, + fileName: companyDocumentVersions.fileName, + mimeType: companyDocumentVersions.mimeType, + sizeBytes: companyDocumentVersions.sizeBytes, + uploadedBy: companyDocumentVersions.uploadedBy, + uploadedAt: companyDocumentVersions.uploadedAt + }) + .from(companyDocumentVersions) + .where( + inArray( + companyDocumentVersions.documentId, + docRows.map((d) => d.id) + ) + ); + for (const v of versionRows) { + const existing = latestByDoc.get(v.documentId); + if (!existing || v.versionNumber > existing.versionNumber) { + latestByDoc.set(v.documentId, v); + } + } + } + + const rows: unknown[][] = [ + [ + 'id', 'category', 'customLabel', 'title', 'description', 'expiresAt', + 'currentVersion', 'currentFilename', 'currentSizeBytes', 'currentMimeType', + 'uploadedBy', 'uploadedAt', 'deletedAt', 'createdAt', 'updatedAt' + ] + ]; + for (const d of docRows) { + const latest = latestByDoc.get(d.id); + rows.push([ + d.id, d.category, d.customLabel ?? '', d.title, d.description ?? '', + d.expiresAt ?? '', + latest?.versionNumber ?? '', + latest?.fileName ?? '', + latest?.sizeBytes ?? '', + latest?.mimeType ?? '', + latest?.uploadedBy ?? '', + latest?.uploadedAt.toISOString() ?? '', + d.deletedAt ? d.deletedAt.toISOString() : '', + d.createdAt.toISOString(), d.updatedAt.toISOString() + ]); + } + zip.file('company_documents.csv', withBom(csvBuild(rows))); + } + // ── projects.csv ──────────────────────────────────── const projectRows = await db .select() diff --git a/src/routes/(app)/companies/[companyId]/+layout.svelte b/src/routes/(app)/companies/[companyId]/+layout.svelte index b378332..65ba9c1 100644 --- a/src/routes/(app)/companies/[companyId]/+layout.svelte +++ b/src/routes/(app)/companies/[companyId]/+layout.svelte @@ -33,7 +33,10 @@ ] : []), ...(data.companyRoles.some((r) => r === 'admin' || r === 'manager' || r === 'accountant') - ? [{ href: `/companies/${data.company.id}/profile`, label: 'Profile' }] + ? [ + { href: `/companies/${data.company.id}/profile`, label: 'Profile' }, + { href: `/companies/${data.company.id}/documents`, label: 'Documents' } + ] : []), ...(data.companyRoles.includes('admin') || data.companyRoles.includes('accountant') ? [{ href: `/companies/${data.company.id}/export`, label: 'Export' }]