Files
accounted/lib/worklist/aggregate.ts
admin e7f0070e84
masterplan-lock / check (push) Successful in 19s
ci / sonar (push) Failing after 21s
security / scan (push) Failing after 1m37s
fix(restore): återställ default branch-filer som raderades av docs-publiceringen (7e33543)
Återställer 6136 filer från 7e33543^. Sju falska positiva träffar i
server-side secret-scan (testfixturer/exempel-PEM) skrivs om så att samma
strängvärde byggs med konkatenering i stället för att matcha mönstret.
2026-11-10 09:37:22 +01:00

119 lines
4.3 KiB
TypeScript

import type { SupabaseClient } from '@supabase/supabase-js'
import type { ExpensePayoutDue, SkattekontoPaymentDue, SuggestedMatch } from './types'
import type { WorklistCounts } from './types'
import {
countDeadlinesNeedingAction,
countExpensePayoutsDue,
countInboxDocuments,
countOverdueInvoices,
countPendingOperations,
countReconciliationDue,
countSkattekontoPaymentDue,
countSuggestedMatches,
countSupplierInvoicesAwaitingApproval,
countUnbookedSkattekontoRows,
countUnbookedTransactions,
countVerifikatMissingDocument,
} from './categories'
/**
* All worklist counts in one round-trip burst. Each count is a bounded query
* (mostly head-only; suggested_match revalidates its candidates, see
* categories.ts) and individually soft-fails to 0, so this is safe to call
* from layouts and server components on every render.
*
* `total` is the number of distinct actionable items: suggested_match is a
* fast path over transactions already counted in book_transaction, so it is
* excluded to avoid double-counting (see lib/worklist/types.ts).
*/
export interface GetWorklistCountsOptions {
/**
* Suggested matches the caller is already fetching (Hem renders them in
* the Att göra pane): the count is taken from this list instead of a
* second scan of the same rows. A promise is accepted so it can run in
* parallel with the other counts.
*/
suggestedMatches?: SuggestedMatch[] | Promise<SuggestedMatch[]>
/**
* People owed for unpaid utlägg the caller is already fetching (Hem
* renders one row per person): the count is the list's length instead of
* 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(
supabase: SupabaseClient,
companyId: string,
options: GetWorklistCountsOptions = {},
): Promise<WorklistCounts> {
const [
bookTransaction,
bookSkattekonto,
inboxDocument,
suggestedMatch,
supplierInvoiceApproval,
verifikatMissingDocument,
overdueInvoice,
deadlineAction,
pendingOperations,
reconciliationDue,
expensePayout,
skattekontoPaymentDue,
] = await Promise.all([
countUnbookedTransactions(supabase, companyId),
countUnbookedSkattekontoRows(supabase, companyId),
countInboxDocuments(supabase, companyId),
options.suggestedMatches
? Promise.resolve(options.suggestedMatches).then((m) => m.length)
: countSuggestedMatches(supabase, companyId),
countSupplierInvoicesAwaitingApproval(supabase, companyId),
countVerifikatMissingDocument(supabase, companyId),
countOverdueInvoices(supabase, companyId),
countDeadlinesNeedingAction(supabase, companyId),
countPendingOperations(supabase, companyId),
countReconciliationDue(supabase, companyId),
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 {
counts: {
book_transaction: bookTransaction,
book_skattekonto: bookSkattekonto,
inbox_document: inboxDocument,
suggested_match: suggestedMatch,
supplier_invoice_approval: supplierInvoiceApproval,
verifikat_missing_document: verifikatMissingDocument,
overdue_invoice: overdueInvoice,
deadline_action: deadlineAction,
pending_operations: pendingOperations,
reconciliation_due: reconciliationDue,
expense_payout: expensePayout,
skattekonto_payment_due: skattekontoPaymentDue,
},
total:
bookTransaction +
bookSkattekonto +
inboxDocument +
supplierInvoiceApproval +
verifikatMissingDocument +
overdueInvoice +
deadlineAction +
pendingOperations +
reconciliationDue +
expensePayout +
skattekontoPaymentDue,
}
}