- Implemented OpeningBalanceResultStep component to display results of the import process, including success messages and error handling. - Created OpeningBalanceUploadStep component for file upload with drag-and-drop support, including validation for accepted file types. - Developed column detection logic in column-detector.ts to identify account number, name, debit, credit, and balance columns based on headers and data. - Added parser functionality in parser.ts to handle parsing of opening balance files, including validation and BAS account matching. - Created tests for column detection and parsing logic to ensure accuracy and reliability. - Defined types for detected columns and parsed rows in types.ts to improve type safety and clarity in the codebase.
75 lines
2.2 KiB
TypeScript
75 lines
2.2 KiB
TypeScript
import { createClient } from '@/lib/supabase/server'
|
|
import { NextResponse } from 'next/server'
|
|
import { parseOpeningBalanceFile } from '@/lib/import/opening-balance/parser'
|
|
import type { DetectedColumns } from '@/lib/import/opening-balance/types'
|
|
|
|
const ALLOWED_EXTENSIONS = ['.xlsx', '.xls', '.csv', '.ods']
|
|
const MAX_FILE_SIZE = 10 * 1024 * 1024 // 10MB
|
|
|
|
/**
|
|
* POST /api/import/opening-balance/parse
|
|
*
|
|
* Accepts an Excel/CSV file via FormData, auto-detects columns,
|
|
* returns parsed opening balance rows with BAS matching.
|
|
*/
|
|
export async function POST(request: Request) {
|
|
const supabase = await createClient()
|
|
|
|
const { data: { user } } = await supabase.auth.getUser()
|
|
if (!user) {
|
|
return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })
|
|
}
|
|
|
|
const formData = await request.formData()
|
|
const file = formData.get('file') as File | null
|
|
const columnOverridesRaw = formData.get('column_overrides') as string | null
|
|
|
|
if (!file) {
|
|
return NextResponse.json({ error: 'Ingen fil bifogad' }, { status: 400 })
|
|
}
|
|
|
|
// Validate file size
|
|
if (file.size > MAX_FILE_SIZE) {
|
|
return NextResponse.json(
|
|
{ error: 'Filen är för stor (max 10MB)' },
|
|
{ status: 400 },
|
|
)
|
|
}
|
|
|
|
// Validate file extension
|
|
const ext = '.' + file.name.split('.').pop()?.toLowerCase()
|
|
if (!ALLOWED_EXTENSIONS.includes(ext)) {
|
|
return NextResponse.json(
|
|
{ error: `Filformatet stöds inte. Tillåtna format: ${ALLOWED_EXTENSIONS.join(', ')}` },
|
|
{ status: 400 },
|
|
)
|
|
}
|
|
|
|
try {
|
|
const buffer = await file.arrayBuffer()
|
|
|
|
// Parse optional column overrides
|
|
let columnOverrides: DetectedColumns | undefined
|
|
if (columnOverridesRaw) {
|
|
try {
|
|
columnOverrides = JSON.parse(columnOverridesRaw)
|
|
} catch {
|
|
return NextResponse.json(
|
|
{ error: 'Ogiltigt kolumnmappningsformat' },
|
|
{ status: 400 },
|
|
)
|
|
}
|
|
}
|
|
|
|
const result = parseOpeningBalanceFile(buffer, file.name, columnOverrides)
|
|
|
|
return NextResponse.json({ data: result })
|
|
} catch (error) {
|
|
console.error('Opening balance parse error:', error)
|
|
return NextResponse.json(
|
|
{ error: error instanceof Error ? error.message : 'Kunde inte tolka filen' },
|
|
{ status: 500 },
|
|
)
|
|
}
|
|
}
|