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
@@ -0,0 +1,38 @@
-- Extend the active-import partial unique index to also release the
-- (company_id, file_hash) slot when a row is marked 'undone'.
--
-- Background: 20260528120100_undo_sie_import.sql introduced the 'undone'
-- status (set by undo_sie_import RPC) but the partial unique index from
-- 20260517150000_sie_imports_active_partial_unique.sql still only
-- excluded 'replaced' and 'failed'. Net effect: a clean undo left the
-- file_hash slot held, so the caller could not re-import the same file
-- afterwards without going through replace_sie_import. Add 'undone' to
-- the predicate so undo + retry works.
--
-- Backfill: prior to this migration, the executor would mark an import
-- 'completed' even when journal_entries_created=0 (see Lookma AB support
-- case 2026-05-28). Those rows hold the slot and block any retry. The
-- companion code change in finalizeImportRecord prevents new occurrences;
-- this backfill heals existing data by flipping every 'completed' row
-- that produced literally zero entries to 'failed' (which the partial
-- index already excludes). Safe by construction — no journal entries
-- were ever created for these rows, so nothing downstream depends on
-- their 'completed' status.
DROP INDEX IF EXISTS public.sie_imports_company_id_file_hash_active_idx;
CREATE UNIQUE INDEX sie_imports_company_id_file_hash_active_idx
ON public.sie_imports (company_id, file_hash)
WHERE status <> ALL (ARRAY['replaced'::text, 'failed'::text, 'undone'::text]);
UPDATE public.sie_imports
SET status = 'failed',
error_message = COALESCE(error_message || '; ', '')
|| 'Backfill 2026-05-29: importen markerades som '
|| '''completed'' men skapade 0 verifikationer. '
|| 'Slot frigjord så filen kan importeras om med korrekta mappningar.'
WHERE status = 'completed'
AND transactions_count = 0
AND opening_balance_entry_id IS NULL;
NOTIFY pgrst, 'reload schema';
@@ -0,0 +1,12 @@
-- Remember the last BAS account a user picked when paying a supplier invoice,
-- so the next mark-paid dialog can default to it instead of forcing the user
-- to re-pick 1930 / 1940 / 2018 / 2893 each time.
--
-- Free-text TEXT column — the existing chart_of_accounts CHECK constraint
-- (4 ASCII digits per BAS standard) is enforced upstream by the Zod schema
-- (accountNumber primitive in lib/api/schemas.ts).
ALTER TABLE company_settings
ADD COLUMN last_supplier_payment_account TEXT;
NOTIFY pgrst, 'reload schema';
@@ -0,0 +1,37 @@
-- Migration: transactions.is_ignored
--
-- Adds an "ignore" flag for bank transactions the user has chosen to suppress
-- from the bank reconciliation view (Rapporter → Bankavstämning) without
-- booking them. Use case: small ränteintäkter, opening-balance adjustments,
-- rounding noise — the user wants the row off the unmatched list but doesn't
-- want to fabricate a verifikation.
--
-- The flag is intentionally orthogonal to `is_business`:
-- - is_business=null → not yet triaged
-- - is_business=true → bokförd som affärstransaktion (has journal_entry_id)
-- - is_business=false → privat uttag (has journal_entry_id, 2013 in EF)
-- - is_ignored=true → "hide from reconciliation, never going to book it"
--
-- An ignored transaction MUST NOT have a journal_entry_id. The check
-- enforces that — once booked, the row has a verifikation and "ignored" is
-- meaningless. Unignoring is just `is_ignored=false`; safe because we never
-- created an entry to reverse.
ALTER TABLE public.transactions
ADD COLUMN IF NOT EXISTS is_ignored BOOLEAN NOT NULL DEFAULT false;
-- An ignored transaction has no journal entry. Without this constraint a
-- categorize → ignore race could leave the row both booked AND hidden from
-- the reconciliation list, which is exactly the silent-divergence pattern
-- bank reconciliation exists to prevent.
ALTER TABLE public.transactions
ADD CONSTRAINT transactions_is_ignored_no_journal_entry
CHECK (is_ignored = false OR journal_entry_id IS NULL);
-- Partial index — most rows will be is_ignored=false, only the small slice
-- of intentionally-skipped transactions need to be looked up by this flag.
CREATE INDEX IF NOT EXISTS idx_transactions_is_ignored
ON public.transactions (company_id, is_ignored)
WHERE is_ignored = true;
NOTIFY pgrst, 'reload schema';
@@ -0,0 +1,53 @@
-- =============================================================================
-- Salary absence: add 'unpaid_leave' (tjänstledighet utan lön)
-- =============================================================================
--
-- The salary engine treats unpaid_leave as a per-day gross deduction (one
-- daily rate per day) and excludes it from semestergrundande tid (SemL 17 §
-- only paid leave types accrue vacation). It complements the existing partial-
-- month employment_start/employment_end proration: that handles new hires and
-- terminations; unpaid_leave handles sabbaticals/leave mid-employment.
ALTER TABLE public.salary_absence_days
DROP CONSTRAINT salary_absence_days_absence_type_check;
ALTER TABLE public.salary_absence_days
ADD CONSTRAINT salary_absence_days_absence_type_check
CHECK (absence_type IN (
'sick', -- sjukfrånvaro
'vab', -- vård av barn (tillfällig föräldrapenning)
'parental', -- föräldraledighet (föräldrapenning)
'pregnancy', -- graviditetspenning
'care_relative', -- närståendepenning
'study', -- studieledig
'unpaid_leave', -- tjänstledighet utan lön
'other_leave'
));
-- The derived absence line items (sick_karens, vab, parental_leave, …) are
-- inserted into salary_line_items by the calculator. unpaid_leave follows the
-- same pattern, so the line-item CHECK must accept it too.
ALTER TABLE public.salary_line_items
DROP CONSTRAINT salary_line_items_item_type_check;
ALTER TABLE public.salary_line_items
ADD CONSTRAINT salary_line_items_item_type_check
CHECK (item_type IN (
'monthly_salary', 'hourly_salary',
'overtime', 'overtime_50', 'overtime_100',
'ob_weekday_evening', 'ob_weekend', 'ob_night', 'ob_holiday',
'bonus', 'commission',
'gross_deduction_pension', 'gross_deduction_other',
'benefit_car', 'benefit_housing', 'benefit_meals',
'benefit_wellness', 'benefit_bike', 'benefit_other',
'sick_karens', 'sick_day2_14', 'sick_day15_plus',
'vab', 'parental_leave', 'unpaid_leave',
'vacation', 'semesterersattning',
'traktamente_taxfree', 'traktamente_taxable',
'mileage_taxfree', 'mileage_taxable',
'net_deduction_advance', 'net_deduction_union',
'net_deduction_benefit_payment', 'net_deduction_other',
'correction', 'other'
));
NOTIFY pgrst, 'reload schema';