feat(expenses): expense claims module (utlägg) (#2145)

Contributed by @joakimhew. Maintainer commits on top: migration re-versioned to 20260904170000 (main's 20260901210000 took the original version), payout batches booked atomically through the create_expense_payout_batch RPC, accounted-api skill regenerated, main merged. Closes #2143.
This commit is contained in:
Joakim Hansson
2026-09-05 13:36:51 +02:00
committed by GitHub
parent f7f40c04e4
commit 397a3b9bca
24 changed files with 5085 additions and 5 deletions
@@ -1,6 +1,7 @@
'use client'
import { useState, useCallback, useEffect, useRef, useMemo } from 'react'
import { useRouter } from 'next/navigation'
import { useCompanySettings } from '@/lib/reference-data/hooks'
import { useTranslations } from 'next-intl'
import { Badge } from '@/components/ui/badge'
@@ -430,6 +431,7 @@ const WorkspaceSkeleton = InvoiceInboxSkeleton
export default function InvoiceInboxWorkspace(_props: WorkspaceComponentProps) {
const { toast } = useToast()
const router = useRouter()
const t = useTranslations('inbox_workspace')
const tStart = useTranslations('start_cards')
const dismissKeyCompanyId = useCompanyOptional()?.company?.id ?? null
@@ -2153,6 +2155,7 @@ export default function InvoiceInboxWorkspace(_props: WorkspaceComponentProps) {
onDelete={() => handleDelete(selected.id)}
onBookDirect={() => setBookDirectOpen(true)}
onCreateSupplierInvoice={() => setCreateSupplierInvoiceOpen(true)}
onRegisterExpense={() => router.push(`/expenses?new=1&inbox_item=${selected.id}`)}
onMatchTransaction={() => setMatchPickerOpen(true)}
onUnmatchTransaction={async () => {
const targetId = selected.id
@@ -3059,6 +3062,7 @@ function FieldsRail({
onDelete,
onBookDirect,
onCreateSupplierInvoice,
onRegisterExpense,
onMatchTransaction,
onUnmatchTransaction,
onAskAssistant,
@@ -3073,6 +3077,7 @@ function FieldsRail({
onDelete: () => void
onBookDirect: () => void
onCreateSupplierInvoice: () => void
onRegisterExpense: () => void
onMatchTransaction: () => void
onUnmatchTransaction: () => Promise<void>
onAskAssistant?: (transactionId: string) => void
@@ -3623,6 +3628,15 @@ function FieldsRail({
För leverantörsskulder du vill följa (periodisering).
</span>
</DropdownMenuItem>
<DropdownMenuItem
onClick={onRegisterExpense}
className="flex flex-col items-start gap-1"
>
<span>Registrera som utlägg</span>
<span className="text-xs text-muted-foreground">
För köp du eller en anställd betalat privat.
</span>
</DropdownMenuItem>
<DropdownMenuItem
onClick={onBookDirect}
className="flex flex-col items-start gap-1"