Fix/user feedback (#210)

* Add delete policies for provider consent tokens and provider OTC

* Add trade name support for companies in settings and documents

* Resolved currency selection issue

* Enhance invoice line display with foreign currency support and update delivery date schema to allow empty values

* Add currency display for journal entries and include currency metadata in transaction creation

* Add trade_name column to company_settings for external display
This commit is contained in:
Mattsson
2026-04-09 16:22:51 +02:00
committed by GitHub
parent b484e9a7b4
commit bb336eba88
31 changed files with 452 additions and 87 deletions
@@ -23,6 +23,9 @@ export interface FormLine {
debit_amount: string
credit_amount: string
line_description: string
currency?: string
amount_in_currency?: number
exchange_rate?: number
}
interface Props {
@@ -159,6 +162,9 @@ export default function JournalEntryForm({
debit_amount: parseFloat(l.debit_amount) || 0,
credit_amount: parseFloat(l.credit_amount) || 0,
line_description: l.line_description || undefined,
...(l.currency ? { currency: l.currency } : {}),
...(l.amount_in_currency != null ? { amount_in_currency: l.amount_in_currency } : {}),
...(l.exchange_rate != null ? { exchange_rate: l.exchange_rate } : {}),
}))
const url = submitUrl ?? '/api/bookkeeping/journal-entries'
+12 -5
View File
@@ -390,11 +390,18 @@ export default function JournalEntryList({ periodId }: Props) {
<span className="text-muted-foreground">
{Number(line.debit_amount) > 0 ? 'Debet' : 'Kredit'}
</span>
<span className="font-mono tabular-nums font-medium">
{Number(line.debit_amount) > 0
? Number(line.debit_amount).toLocaleString('sv-SE', { minimumFractionDigits: 2 })
: Number(line.credit_amount).toLocaleString('sv-SE', { minimumFractionDigits: 2 })}
</span>
<div className="text-right">
<span className="font-mono tabular-nums font-medium">
{Number(line.debit_amount) > 0
? Number(line.debit_amount).toLocaleString('sv-SE', { minimumFractionDigits: 2 })
: Number(line.credit_amount).toLocaleString('sv-SE', { minimumFractionDigits: 2 })}
</span>
{line.currency && line.currency !== 'SEK' && line.amount_in_currency != null && (
<span className="block text-xs text-muted-foreground font-mono tabular-nums">
{Number(line.amount_in_currency).toLocaleString('sv-SE', { minimumFractionDigits: 2 })} {line.currency}
</span>
)}
</div>
</div>
</div>
)
+12
View File
@@ -28,6 +28,18 @@ export function CompanyInfoForm({ settings }: CompanyInfoFormProps) {
<p className="text-xs text-muted-foreground">Kan inte ändras efter att kontot skapats</p>
)}
</div>
<div className="space-y-2">
<Label htmlFor="trade_name">Handelsnamn</Label>
<Input
id="trade_name"
name="trade_name"
defaultValue={settings.trade_name || ''}
placeholder="Visas på fakturor istället för företagsnamnet"
/>
<p className="text-xs text-muted-foreground">
Valfritt. Visas som huvudnamn på fakturor och e-post, med det juridiska namnet i parentes.
</p>
</div>
<div className="space-y-2">
<Label htmlFor="org_number">Organisationsnummer</Label>
<Input
@@ -11,6 +11,7 @@ import JournalEntryForm from '@/components/bookkeeping/JournalEntryForm'
import DocumentUploadZone from '@/components/bookkeeping/DocumentUploadZone'
import type { UploadedFile } from '@/components/bookkeeping/DocumentUploadZone'
import type { FormLine } from '@/components/bookkeeping/JournalEntryForm'
import { resolveSekAmount, buildCurrencyMetadata } from '@/lib/bookkeeping/currency-utils'
import type { TransactionWithInvoice } from './transaction-types'
interface TransactionBookingDialogProps {
@@ -21,21 +22,40 @@ interface TransactionBookingDialogProps {
}
function buildInitialLines(transaction: TransactionWithInvoice): FormLine[] {
const amount = Math.round(Math.abs(transaction.amount_sek ?? transaction.amount) * 100) / 100
const amountStr = amount.toFixed(2)
const sekAmount = Math.round(Math.abs(resolveSekAmount(
transaction.amount,
transaction.amount_sek,
transaction.currency,
transaction.exchange_rate
)) * 100) / 100
const amountStr = sekAmount.toFixed(2)
const isExpense = transaction.amount < 0
if (isExpense) {
return [
{ account_number: '1930', debit_amount: '', credit_amount: amountStr, line_description: 'Företagskonto' },
{ account_number: '', debit_amount: amountStr, credit_amount: '', line_description: '' },
]
const isForeign = !!transaction.currency && transaction.currency !== 'SEK'
const currencyMeta = isForeign
? buildCurrencyMetadata(
transaction.currency,
Math.abs(transaction.amount),
transaction.exchange_rate
)
: {}
const bankLine: FormLine = {
account_number: '1930',
debit_amount: isExpense ? '' : amountStr,
credit_amount: isExpense ? amountStr : '',
line_description: 'Företagskonto',
...currencyMeta,
}
return [
{ account_number: '1930', debit_amount: amountStr, credit_amount: '', line_description: 'Företagskonto' },
{ account_number: '', debit_amount: '', credit_amount: amountStr, line_description: '' },
]
const counterLine: FormLine = {
account_number: '',
debit_amount: isExpense ? amountStr : '',
credit_amount: isExpense ? '' : amountStr,
line_description: '',
}
return isExpense ? [bankLine, counterLine] : [bankLine, counterLine]
}
export default function TransactionBookingDialog({
@@ -6,7 +6,7 @@ import { Button } from '@/components/ui/button'
import { Badge } from '@/components/ui/badge'
import { Checkbox } from '@/components/ui/checkbox'
import { cn, formatCurrency, formatDate } from '@/lib/utils'
import { ArrowUpRight, ArrowDownRight, FileText, Loader2, Paperclip } from 'lucide-react'
import { ArrowUpRight, ArrowDownRight, FileText, Loader2, Paperclip, Trash2 } from 'lucide-react'
import { Tooltip, TooltipTrigger, TooltipContent, TooltipProvider } from '@/components/ui/info-tooltip'
import { getAccountName, formatAccountWithName } from '@/lib/bookkeeping/client-account-names'
import { getTemplateById } from '@/lib/bookkeeping/booking-templates'
@@ -26,7 +26,7 @@ interface TransactionInboxCardProps {
onMarkPrivate: (id: string) => void
onOpenMatchDialog: (transaction: TransactionWithInvoice) => void
onOpenCategoryDialog: (transaction: TransactionWithInvoice) => void
onDelete?: (id: string) => void
onOpenQuickReview?: (transaction: TransactionWithInvoice, suggestion: SuggestedCategory) => void
onOpenTemplateReview?: (transaction: TransactionWithInvoice, templateId: string) => void
onToggleSelect: (id: string) => void
@@ -45,7 +45,7 @@ export default function TransactionInboxCard({
onMarkPrivate,
onOpenMatchDialog,
onOpenCategoryDialog,
onDelete,
onOpenQuickReview,
onOpenTemplateReview,
onToggleSelect,
@@ -60,6 +60,7 @@ export default function TransactionInboxCard({
const isUncategorized = transaction.is_business === null && !transaction.journal_entry_id
const showCheckbox = isBatchMode && isUncategorized
const hasDocumentMatch = !!transaction.matched_inbox_item
const isManualTransaction = !transaction.bank_connection_id && !transaction.import_source && !transaction.journal_entry_id
function handleSuggestionClick(suggestion: SuggestedCategory) {
if (onOpenQuickReview) {
@@ -249,6 +250,20 @@ export default function TransactionInboxCard({
>
Välj mall...
</Button>
{/* Delete button — only for manually added, unbooked transactions */}
{isManualTransaction && onDelete && (
<Button
size="sm"
variant="ghost"
className="h-9 w-9 p-0 ml-auto text-muted-foreground hover:text-destructive"
onClick={() => onDelete(transaction.id)}
disabled={isProcessing || isDisabled}
aria-label="Ta bort transaktion"
>
<Trash2 className="h-4 w-4" />
</Button>
)}
</div>
)}
</CardContent>