feat(worklist): the next Skatteverket payment with bankgiro, OCR and due date under Att göra on Hem (#2435)

Part (a) of #2187. A twelfth worklist category, skattekonto_payment_due:
the earliest upcoming skattekonto charge whose sum exceeds the last synced
saldo, computed once in lib/worklist (server-side twin of the /skattekonto
page's Nästa dragning math) and rendered as one Betala row on Hem with the
shortfall, bankgiro 5050-1055, the OCR reference and the due date.

The row appears only when money has to move: a saldo that covers the charge
yields nothing, and no upcoming charge yields nothing. Ignored rows take
part, since Skatteverket draws them regardless of our flag. Without a
balance snapshot the full charge is the amount. Without an org number the
row keeps its bankgiro and date and drops the OCR.

No table, route or migration: /api/worklist/counts picks the category up
through getWorklistCounts, and Hem passes the computed row into the same
options wave as the expense payouts.

Part (b), a betalfil for the skattekonto payment, stays a follow-up: the
existing payment-file route is AGI-scoped.


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

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-08 21:25:01 +02:00
committed by GitHub
co-authored by Jakob Wennberg Claude Fable 5.1
parent a7dcaac6ad
commit 20925f6c65
10 changed files with 353 additions and 9 deletions
+15 -2
View File
@@ -1,5 +1,5 @@
import type { SupabaseClient } from '@supabase/supabase-js'
import type { ExpensePayoutDue, SuggestedMatch } from './types'
import type { ExpensePayoutDue, SkattekontoPaymentDue, SuggestedMatch } from './types'
import type { WorklistCounts } from './types'
import {
countDeadlinesNeedingAction,
@@ -8,6 +8,7 @@ import {
countOverdueInvoices,
countPendingOperations,
countReconciliationDue,
countSkattekontoPaymentDue,
countSuggestedMatches,
countSupplierInvoicesAwaitingApproval,
countUnbookedSkattekontoRows,
@@ -39,6 +40,12 @@ export interface GetWorklistCountsOptions {
* a second scan of expense_claims.
*/
expensePayoutsDue?: ExpensePayoutDue[] | Promise<ExpensePayoutDue[]>
/**
* The next uncovered skattekonto charge the caller is already fetching
* (Hem renders it as one Betala row): the count is 1 or 0 from that value
* instead of a second scan. Pass null for "nothing to pay in".
*/
skattekontoPaymentDue?: SkattekontoPaymentDue | null | Promise<SkattekontoPaymentDue | null>
}
export async function getWorklistCounts(
@@ -58,6 +65,7 @@ export async function getWorklistCounts(
pendingOperations,
reconciliationDue,
expensePayout,
skattekontoPaymentDue,
] = await Promise.all([
countUnbookedTransactions(supabase, companyId),
countUnbookedSkattekontoRows(supabase, companyId),
@@ -74,6 +82,9 @@ export async function getWorklistCounts(
options.expensePayoutsDue
? Promise.resolve(options.expensePayoutsDue).then((p) => p.length)
: countExpensePayoutsDue(supabase, companyId),
options.skattekontoPaymentDue !== undefined
? Promise.resolve(options.skattekontoPaymentDue).then((p) => (p ? 1 : 0))
: countSkattekontoPaymentDue(supabase, companyId),
])
return {
@@ -89,6 +100,7 @@ export async function getWorklistCounts(
pending_operations: pendingOperations,
reconciliation_due: reconciliationDue,
expense_payout: expensePayout,
skattekonto_payment_due: skattekontoPaymentDue,
},
total:
bookTransaction +
@@ -100,6 +112,7 @@ export async function getWorklistCounts(
deadlineAction +
pendingOperations +
reconciliationDue +
expensePayout,
expensePayout +
skattekontoPaymentDue,
}
}