Bug/skv connection (#430)

* feat(invoices): implement öresavrundning logic and next invoice number preview

- Added `getDisplayTotal` utility to handle rounding for SEK invoices based on company settings.
- Updated `InvoicesPage` to utilize the new rounding logic when displaying totals.
- Introduced `peek_next_invoice_number` function to allow previewing the next invoice number without consuming the sequence.
- Modified invoice number generation to remove the year prefix and prevent truncation of numbers exceeding three digits.
- Enhanced tests for invoice number generation and rounding functionality to ensure correctness.
- Updated PDF template to reflect new rounding logic for totals and display appropriate values.
- Adjusted company switcher to hide options in sandbox mode.
- Improved error handling and logging in sandbox seeding process.

* fix(skatteverket): remove unused scope labels from SCOPE_LABELS and DEFAULT_SCOPES

* feat(skatteverket): add token revocation handling and disconnect functionality
This commit is contained in:
Mattsson
2026-05-10 14:40:16 +02:00
committed by GitHub
parent 0ee5219b6c
commit d7a7ddae2c
4 changed files with 93 additions and 6 deletions
+45 -3
View File
@@ -10,6 +10,7 @@ import {
Link2Off,
Loader2,
Lock,
PlugZap,
Send,
ShieldAlert,
Unlock,
@@ -249,6 +250,29 @@ export function AGIPanel(props: AGIPanelProps) {
kvittensTimers.current.push(setTimeout(poll, 300_000))
}, [arbetsgivare, period, fetchSubmission, onChange])
const handleDisconnect = useCallback(async () => {
setActionLoading('disconnect')
setError(null)
setSuccess(null)
try {
const res = await fetch('/api/extensions/ext/skatteverket/disconnect', {
method: 'POST',
})
if (!res.ok) {
const json = await res.json().catch(() => ({}))
setError(json.error || `Kunde inte koppla bort (${res.status})`)
return
}
setSuccess('Anslutningen mot Skatteverket har kopplats bort.')
await fetchStatus()
await fetchSubmission()
} catch (e) {
setError(e instanceof Error ? e.message : 'Kunde inte koppla bort')
} finally {
setActionLoading(null)
}
}, [fetchStatus, fetchSubmission])
const handleConnect = () => {
// Open the BankID OAuth flow in a centered popup. The callback page
// detects `window.opener` and posts back a `skatteverket-oauth-success`
@@ -563,9 +587,27 @@ export function AGIPanel(props: AGIPanelProps) {
<CardHeader>
<CardTitle className="flex items-center justify-between text-base">
<span>Arbetsgivardeklaration (AGI)</span>
<span className="flex items-center gap-1 text-xs font-normal text-muted-foreground">
<CheckCircle2 className="h-3.5 w-3.5 text-emerald-600" />
Ansluten
<span className="flex items-center gap-2 text-xs font-normal text-muted-foreground">
<span className="flex items-center gap-1">
<CheckCircle2 className="h-3.5 w-3.5 text-emerald-600" />
Ansluten
</span>
{!readOnly && (
<button
type="button"
onClick={handleDisconnect}
disabled={actionLoading === 'disconnect'}
className="inline-flex items-center gap-1 rounded border border-border px-1.5 py-0.5 text-[11px] font-normal text-muted-foreground transition-colors hover:border-destructive/50 hover:text-destructive disabled:cursor-not-allowed disabled:opacity-50"
title="Koppla bort anslutningen mot Skatteverket"
>
{actionLoading === 'disconnect' ? (
<Loader2 className="h-3 w-3 animate-spin" />
) : (
<PlugZap className="h-3 w-3" />
)}
Koppla bort
</button>
)}
</span>
</CardTitle>
</CardHeader>