fix(expenses): review follow-ups from #2333 (#2352)

- The owner's claimant key is trimmed and lower-cased, the same rule the
  payout RPC applies, so "Jakob" and "jakob " are one person with one
  Att göra row and one exact-amount match.
- The inbox pages through every registered claim (fetchAllRows) before
  pairing, so a long backlog can never understate a person's debt.
- A foreign receipt's VAT field is locked at 0 and 0 is what is submitted.
- Transport failures in the one-click and picker confirms show the
  destructive toast instead of failing silently.
- The open-claims flag is set only after the stale-fetch guard, and a
  payout match decrements the inbox count like every other row exit.
- Test: reset the live-link mock before the bank_line junction case.
- Wording: "Återbetalning av utlägg".

Co-authored-by: Jakob Wennberg <311770904+jakobwennberg-oss@users.noreply.github.com>
Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
Jakob Wennberg
2026-09-06 20:19:18 +02:00
committed by GitHub
co-authored by Jakob Wennberg Claude Fable 5.1
parent 1a725c121a
commit 3b9daf2606
10 changed files with 66 additions and 24 deletions
@@ -25,7 +25,7 @@ import { useToast } from '@/components/ui/use-toast'
import { getErrorMessage } from '@/lib/errors/get-error-message'
import { formatCurrency, formatDate } from '@/lib/utils'
import { roundOre } from '@/lib/money'
import { groupExpenseClaimsByPerson } from '@/lib/expenses/expense-payout-candidates'
import { expenseClaimantKey, groupExpenseClaimsByPerson } from '@/lib/expenses/expense-payout-candidates'
import type { ExpensePayoutDue } from '@/lib/worklist/types'
import type { TransactionWithInvoice } from './transaction-types'
@@ -98,7 +98,7 @@ export default function ExpenseClaimPickerDialog({ open, onOpenChange, transacti
const personClaims = useMemo(
() =>
claims
.filter((c) => (c.employee_id ?? `owner:${c.claimant_name}`) === personKey)
.filter((c) => expenseClaimantKey(c) === personKey)
.sort((a, b) => a.expense_date.localeCompare(b.expense_date)),
[claims, personKey],
)
@@ -136,6 +136,12 @@ export default function ExpenseClaimPickerDialog({ open, onOpenChange, transacti
return
}
onMatched(transaction.id, result.journal_entry_id, personKey)
} catch (error) {
toast({
title: t('failed_title'),
description: getErrorMessage(error, { context: 'transaction' }),
variant: 'destructive',
})
} finally {
setSubmitting(false)
}