Fix/m sprint fixes (#613)

* fix(dashboard): exclude ignored and already-triaged transactions from stale count

The "Gamla transaktioner" widget counted transactions that had been ignored
or already marked as is_business=true but not yet booked, so users saw a
nag for a row they had already dealt with — and the /transactions inbox
correctly hid it. Align the count with the inbox criterion (is_business
IS NULL, is_ignored = false) so the widget clears when the row leaves
the inbox.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* fix(transactions): read entity_type from settings response wrapper

The transactions page read entityRes.entity_type directly, but
/api/settings returns { data: { entity_type, ... } }. The expression
was always undefined, so setEntityType never fired and entityType
stayed at its initial 'enskild_firma'. The template picker's
entity_type filter then dropped every aktiebolag-tagged user template
for AB customers — only entity_type='all' templates made it through.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* stale templates
bank sync
journal entry from transaction

* fixed pr comments

* fixed pr comment

---------

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Mattsson
2026-05-30 01:28:41 +02:00
committed by GitHub
co-authored by Claude Opus 4.7
parent fc7a46c3f2
commit ea1bf01f1e
76 changed files with 5474 additions and 532 deletions
+29 -2
View File
@@ -20,20 +20,47 @@ export async function GET(request: Request) {
const currency = searchParams.get('currency') || undefined
const dateFrom = searchParams.get('date_from') || undefined
const dateTo = searchParams.get('date_to') || undefined
// When set, return only ignored rows — used by the reconciliation view to
// surface a "Visa ignorerade" undo list. The default (no param) behaviour
// continues to exclude ignored rows from unmatched results.
const onlyIgnored = searchParams.get('only_ignored') === 'true'
// account_number is accepted for API symmetry with the reconciliation status
// endpoint; transactions don't carry a cash_account FK today (PSD2 account
// identity is embedded in external_id), so we use it to derive a default
// currency when the caller didn't supply one. Anything more precise needs
// the cash_account_id backfill tracked as Tier 4.
const accountNumberParam = searchParams.get('account_number') || undefined
let derivedCurrency = currency
if (!derivedCurrency && accountNumberParam) {
const { data: cashAccount } = await supabase
.from('cash_accounts')
.select('currency')
.eq('company_id', companyId)
.eq('ledger_account', accountNumberParam)
.maybeSingle()
if (cashAccount?.currency) derivedCurrency = cashAccount.currency as string
}
let query = supabase
.from('transactions')
.select('id, date, description, amount, currency, amount_sek, exchange_rate, reference, journal_entry_id, reconciliation_method')
.select('id, date, description, amount, currency, amount_sek, exchange_rate, reference, journal_entry_id, reconciliation_method, is_ignored')
.eq('company_id', companyId)
// unmatched and reconciled are mutually exclusive — unmatched wins if both set
if (unmatched) {
query = query.is('journal_entry_id', null)
// Hide rows the user has explicitly suppressed from the reconciliation
// view. Other callers (e.g. BookDirectlyDialog) also benefit — once
// ignored, the row stops surfacing in the "to book" funnel everywhere.
if (!onlyIgnored) query = query.eq('is_ignored', false)
} else if (reconciled) {
query = query.not('journal_entry_id', 'is', null)
}
if (currency) query = query.eq('currency', currency)
if (onlyIgnored) query = query.eq('is_ignored', true)
if (derivedCurrency) query = query.eq('currency', derivedCurrency)
if (dateFrom) query = query.gte('date', dateFrom)
if (dateTo) query = query.lte('date', dateTo)