'use client' import { useState, useEffect } from 'react' import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogFooter, } from '@/components/ui/dialog' import { Button } from '@/components/ui/button' import { Input } from '@/components/ui/input' import { Badge } from '@/components/ui/badge' import { AccountNumber } from '@/components/ui/account-number' import AccountCombobox from '@/components/bookkeeping/AccountCombobox' import { useToast } from '@/components/ui/use-toast' import { Plus, Trash2 } from 'lucide-react' import type { JournalEntry, JournalEntryLine, BASAccount } from '@/types' interface CorrectionLine { account_number: string debit_amount: string credit_amount: string line_description: string } interface Props { entry: JournalEntry open: boolean onOpenChange: (open: boolean) => void onCorrected: () => void } export default function CorrectionEntryDialog({ entry, open, onOpenChange, onCorrected }: Props) { const { toast } = useToast() const [accounts, setAccounts] = useState([]) const [lines, setLines] = useState([]) const [isSubmitting, setIsSubmitting] = useState(false) const originalLines = ((entry.lines || []) as JournalEntryLine[]) .slice() .sort((a, b) => a.sort_order - b.sort_order) useEffect(() => { if (open) { // Pre-fill with original entry's lines setLines( originalLines.map((l) => ({ account_number: l.account_number, debit_amount: Number(l.debit_amount) > 0 ? String(Number(l.debit_amount)) : '', credit_amount: Number(l.credit_amount) > 0 ? String(Number(l.credit_amount)) : '', line_description: l.line_description || '', })) ) fetchAccounts() } }, [open, entry.id]) // eslint-disable-line react-hooks/exhaustive-deps async function fetchAccounts() { try { const res = await fetch('/api/bookkeeping/accounts') const { data } = await res.json() setAccounts(data || []) } catch { // Accounts will be empty — user can still type account numbers manually } } const updateLine = (index: number, field: keyof CorrectionLine, value: string) => { setLines((prev) => prev.map((l, i) => (i === index ? { ...l, [field]: value } : l))) } const addLine = () => { setLines((prev) => [...prev, { account_number: '', debit_amount: '', credit_amount: '', line_description: '' }]) } const removeLine = (index: number) => { setLines((prev) => prev.filter((_, i) => i !== index)) } const totalDebit = lines.reduce((sum, l) => sum + (parseFloat(l.debit_amount) || 0), 0) const totalCredit = lines.reduce((sum, l) => sum + (parseFloat(l.credit_amount) || 0), 0) const roundedDebit = Math.round(totalDebit * 100) / 100 const roundedCredit = Math.round(totalCredit * 100) / 100 const isBalanced = roundedDebit === roundedCredit && roundedDebit > 0 const hasValidLines = lines.length >= 2 && lines.every((l) => l.account_number.length === 4) async function handleSubmit() { if (!isBalanced || !hasValidLines) return setIsSubmitting(true) try { const apiLines = lines.map((l) => ({ account_number: l.account_number, debit_amount: parseFloat(l.debit_amount) || 0, credit_amount: parseFloat(l.credit_amount) || 0, line_description: l.line_description || undefined, })) const res = await fetch(`/api/bookkeeping/journal-entries/${entry.id}/correct`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ lines: apiLines }), }) if (!res.ok) { const { error } = await res.json() throw new Error(error || 'Failed to create correction') } toast({ title: 'Ändringsverifikation skapad', description: 'Storno och rättelse har bokförts.' }) onOpenChange(false) onCorrected() } catch (err) { toast({ title: 'Fel', description: err instanceof Error ? err.message : 'Kunde inte skapa ändringsverifikation', variant: 'destructive', }) } finally { setIsSubmitting(false) } } return ( Skapa ändringsverifikation {/* Original entry (read-only) */}
{entry.voucher_series}{entry.voucher_number} {entry.entry_date} Original

{entry.description}

{originalLines.map((line) => ( ))}
Konto Beskrivning Debet Kredit
{line.line_description || ''} {Number(line.debit_amount) > 0 ? Number(line.debit_amount).toLocaleString('sv-SE', { minimumFractionDigits: 2 }) : ''} {Number(line.credit_amount) > 0 ? Number(line.credit_amount).toLocaleString('sv-SE', { minimumFractionDigits: 2 }) : ''}
{/* Divider */}
{/* Corrected lines (editable) */}

Rättade rader

{lines.map((line, index) => (
updateLine(index, 'account_number', v)} /> updateLine(index, 'line_description', e.target.value)} placeholder="Beskrivning" className="h-8" /> updateLine(index, 'debit_amount', e.target.value)} placeholder="Debet" className="h-8 text-right" min={0} step="0.01" /> updateLine(index, 'credit_amount', e.target.value)} placeholder="Kredit" className="h-8 text-right" min={0} step="0.01" />
))}
{/* Balance summary */}
Debet: {roundedDebit.toLocaleString('sv-SE', { minimumFractionDigits: 2 })}
Kredit: {roundedCredit.toLocaleString('sv-SE', { minimumFractionDigits: 2 })}
{!isBalanced && roundedDebit + roundedCredit > 0 && (

Debet och kredit måste vara lika och större än 0.

)}
) }