fix(expenses): review follow-ups from #2317 (#2326)

- The Utlägg nav row is computed server-side, so the first booked claim
  now refreshes the App Router tree instead of staying hidden until a
  full reload.
- The dialog's default date is the local calendar date; toISOString() is
  UTC and dated a receipt booked after midnight CEST to the previous day.
- listExpensePayoutsDue pages through every registered claim with
  fetchAllRows instead of stopping at 500 rows: a person omitted or a
  total understated there is money the company owes someone.
- The attention resource's payout instruction names the liability
  account per row (2893 / 2018 / 2820) instead of only 2893/2820.


Claude-Session: https://claude.ai/code/session_01P8YsvPqjfGxGZUkGeBVUWQ

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-05 16:43:53 +02:00
committed by GitHub
co-authored by Jakob Wennberg Claude Fable 5.1
parent eb2ae1da17
commit 41a5728ca7
4 changed files with 36 additions and 23 deletions
@@ -3,6 +3,7 @@
import { useState, useCallback, useEffect, useRef, useMemo } from 'react'
import { useCompanySettings } from '@/lib/reference-data/hooks'
import { useTranslations } from 'next-intl'
import { useRouter } from 'next/navigation'
import { Badge } from '@/components/ui/badge'
import { Button } from '@/components/ui/button'
import { Checkbox } from '@/components/ui/checkbox'
@@ -495,6 +496,9 @@ export default function InvoiceInboxWorkspace(_props: WorkspaceComponentProps) {
const [bookDirectOpen, setBookDirectOpen] = useState(false)
// "Vem betalade?" answered with a person: the utlägg confirm step.
const [registerExpensePayer, setRegisterExpensePayer] = useState<ExpensePayer | null>(null)
// The layout computes the Utlägg nav gate (hasExpenseClaims) on the server;
// the first booked claim must re-run it or the row stays hidden until reload.
const router = useRouter()
// Bulk-book selected underlag (Modell B): the "Bokför valda" selection-bar
// action. The dialog filters the selection to bookable items itself.
const [bulkBookOpen, setBulkBookOpen] = useState(false)
@@ -2244,6 +2248,7 @@ export default function InvoiceInboxWorkspace(_props: WorkspaceComponentProps) {
payer={registerExpensePayer}
onSuccess={async () => {
await Promise.all([fetchItems(), handleSelect(selected.id)])
router.refresh()
}}
/>
)}
@@ -62,7 +62,12 @@ interface Props {
const OWNER_FALLBACK_NAME = 'Ägare'
function todayIso(): string {
return new Date().toISOString().slice(0, 10)
// Local calendar date: toISOString() is UTC and would date a receipt booked
// after midnight CEST to the previous day (wrong period, wrong FX rate).
const now = new Date()
const mm = String(now.getMonth() + 1).padStart(2, '0')
const dd = String(now.getDate()).padStart(2, '0')
return `${now.getFullYear()}-${mm}-${dd}`
}
function parseAmount(raw: string): number {