diff --git a/DECISIONS.md b/DECISIONS.md index a696926f..4c8d8778 100644 --- a/DECISIONS.md +++ b/DECISIONS.md @@ -1531,6 +1531,7 @@ One line per decision: `[YYYY-MM-DD] : `. Appended by agents and [2026-09-03] Kontakter is not a user-facing noun (founder, after the register walkthrough): the registers people see stay Leverantörer and Kunder, the new page is the queue 'Förslag från bokföringen' plus 'Bara i bokföringen', and confirming a suggestion creates the leverantör or kund row directly (promote_parties). A confirmed party with no role never appears in the UI. The party model underneath is unchanged [2026-09-03] AGI redovisningsperiod = the payout month (agiReportingPeriod on payment_date), not salary_runs.period_*: Skatteverket files per the month the pay went out (kontantprincipen), so lön i efterskott (August work paid 25 September) is declared in September. The in-period payment-date guard (dashboard PATCH, lib/salary/update-run.ts, v1 PATCH, RunHeader min/max) is lifted rather than widened: its only stated reason was that the AGI keyed on period_*, and any residual month window would bite the next efterskott variant. Existing agi_declarations rows keep their stored period (no backfill): a declaration already filed under the earned month is a real-world correction with Skatteverket, not a re-key. New AGI_PERIOD_CONFLICT (409) refuses to overwrite a live run's declaration for the same payout month, since one month's AGI must cover every payment that month and the generator cannot merge runs. Issue #2191. [2026-09-03] The cursor:// deeplink is its own allowlist provider (cursor_deeplink) rendered "Din egen dator" and never "Verifierad", after the skeptic, CodeRabbit and Superagent all made the same point: a custom scheme can be claimed by any local app (RFC 8252 section 8.4), so it carries loopback trust, not vendor trust, and the consent page must not say otherwise; https://www.cursor.com/... keeps the verified label. Same pass fixed the consent-page CSP for custom schemes: new URL('cursor://...').origin is the string "null", so form-action became `'self' null` and Chromium would have blocked the post-consent 303 (correctness skeptic refutation); the header now uses the scheme-source (`cursor:`) when the origin is opaque. Not done: rejecting a missing code_challenge at /authorize. A code minted without one is unexchangeable (verifyPkce against an empty challenge is always false, now pinned by a test), so it is fail-closed; making it fail earlier is a separate change touching every client. +[2026-09-03] The Lucide Building2 glyph is retired app-wide (founder, from the register walkthrough). Suppliers use Truck, companies and company-scoped things use Briefcase, banks use Landmark; the extension manifest icon name changed with it [2026-09-03] settleInvoicePayment writes the invoice_payments row BEFORE the CAS status update and removes it in both failure branches, instead of inserting after the update: the kontantmetod cut-off reads invoice_payments only, so a paid invoice without a row is the #2019 defect itself; failing closed on the insert (voucher storno + INVOICE_PAID_BOOK_FAILED) keeps GL, sub-ledger and invoice status in step. The #2019 backfill inserts only where exactly one posted payment voucher exists (invoice_paid / invoice_cash_payment with source_id = invoice); zero or several vouchers are reported, never guessed, and every row is tagged backfill:#2019 in notes so one DELETE reverts a run. [2026-09-03] #2019 skeptic round: the invoice_payments row is written by one helper (lib/invoices/invoice-payment-row.ts) from all four transaction-less settlement paths (dashboard, v1, MCP mark-paid, Stripe), with amount = applied amount (new paid_amount minus prior) rather than cash received, so a 3740 öre absorption never produces a negative fordran in the cut-off or a wrong storno restore. A payment row with transaction_id NULL does NOT count as "reconciled to a bank line" in the two duplicate detectors: the bank line for a manual settlement arrives later and the voucher must still be offered as a twin. The backfill dates rows from the voucher entry_date (paid_at was wall-clock before #1332), refuses rows that disagree with the voucher's 1510 credit / settlement debit, reports partially covered invoices (rows_short) instead of patching them, and records each executed run in behandlingshistorik (new event type InvoicePaymentRowBackfilled, migration 20260903180000). Not done here, pre-existing: the bank-match and pending-operations match paths still store cash received as the row amount, and the kontantmetod cut-off ignores ROT/RUT deduction_total (1513 share shows as outstanding); both filed as follow-ups. [2026-09-03] Enable Banking ASPSP_ERROR ladder (#2202): the widest history window a bank has answered is stored per account as accounts_data[].accepted_history_days (no migration; same write-back as dedup_scope and the balance), not as an absolute date, because a bank's limit is a width from today and an absolute known-good date would age into a genuine width rejection. On a rejected window: no wider than accepted = bank unavailable after ONE call; wider = one retry at the accepted width, then unavailable (was up to five calls). AspspUnavailableError maps to 503 BANK_UNAVAILABLE on the web sync route with copy that says the connection does not need renewing; the agent path keeps the contract code BANK_SYNC_FAILED (adding a code touches core contract + structured-errors + v1 docs, left for a follow-up) but no longer persists renewal advice. The envelope's `detail` field is NOT used as a signal: one sample, "Unknown error", identical to a width rejection. A narrowed sync now returns history_from so the UI can say from which date it is complete. diff --git a/app/(dashboard)/help/page.tsx b/app/(dashboard)/help/page.tsx index 628068fc..b5e4553e 100644 --- a/app/(dashboard)/help/page.tsx +++ b/app/(dashboard)/help/page.tsx @@ -12,12 +12,13 @@ import { BookOpen, Receipt, Calculator, - Building2, FileText, FileDown, ExternalLink, ChevronDown, ChevronUp, + Briefcase, + Landmark, } from 'lucide-react' import { cn } from '@/lib/utils' import { SupportLink } from '@/components/ui/support-link' @@ -214,8 +215,8 @@ const categoryConfig = { moms: { labelKey: 'category_moms', icon: Receipt, color: 'bg-secondary text-muted-foreground' }, faktura: { labelKey: 'category_faktura', icon: FileText, color: 'bg-secondary text-muted-foreground' }, bokföring: { labelKey: 'category_bokforing', icon: BookOpen, color: 'bg-secondary text-muted-foreground' }, - bank: { labelKey: 'category_bank', icon: Building2, color: 'bg-secondary text-muted-foreground' }, - företag: { labelKey: 'category_foretag', icon: Building2, color: 'bg-secondary text-muted-foreground' }, + bank: { labelKey: 'category_bank', icon: Landmark, color: 'bg-secondary text-muted-foreground' }, + företag: { labelKey: 'category_foretag', icon: Briefcase, color: 'bg-secondary text-muted-foreground' }, } function TermCard({ term, isExpanded, onToggle }: { term: GlossaryTerm; isExpanded: boolean; onToggle: () => void }) { diff --git a/app/(dashboard)/suppliers/page.tsx b/app/(dashboard)/suppliers/page.tsx index c98d1498..b1e8d3e2 100644 --- a/app/(dashboard)/suppliers/page.tsx +++ b/app/(dashboard)/suppliers/page.tsx @@ -13,7 +13,7 @@ import { Skeleton } from '@/components/ui/skeleton' import { TH_CLASS, TD_CLASS } from '@/components/ui/dry-table' import { ReportExportMenu } from '@/components/reports/ReportExportMenu' import { useToast } from '@/components/ui/use-toast' -import { Plus, Building2, Lock } from 'lucide-react' +import { Plus, Lock, Truck } from 'lucide-react' import Link from 'next/link' import { cn } from '@/lib/utils' import { useCompany } from '@/contexts/CompanyContext' @@ -194,13 +194,13 @@ export default function SuppliersPage() { ) : filteredSuppliers.length === 0 ? ( searchTerm ? ( {t('no_search_results_description', { term: searchTerm })}} /> ) : (
- +

{invite.companyName}

@@ -309,7 +309,7 @@ export default function InvitePage() {
- +

{invite.companyName}

@@ -337,7 +337,7 @@ export default function InvitePage() {
- +

{invite.companyName}

@@ -365,7 +365,7 @@ export default function InvitePage() {
- +

{invite.companyName}

diff --git a/components/bookkeeping/BookingTemplatePicker.tsx b/components/bookkeeping/BookingTemplatePicker.tsx index afa52301..ec1c3ba5 100644 --- a/components/bookkeeping/BookingTemplatePicker.tsx +++ b/components/bookkeeping/BookingTemplatePicker.tsx @@ -12,7 +12,7 @@ import { DialogTitle, DialogTrigger, } from '@/components/ui/dialog' -import { BookOpen, Search, Building2, Users, Globe } from 'lucide-react' +import { BookOpen, Search, Users, Globe, Briefcase } from 'lucide-react' import { TEMPLATE_CATEGORY_LABELS, SCOPE_LABELS, getTemplateScope, applyTemplate } from '@/lib/bookkeeping/template-library' import type { BookingTemplateCategory, EntityType } from '@/types' import type { FormLine } from '@/components/bookkeeping/JournalEntryForm' @@ -28,7 +28,7 @@ interface Props { const SCOPE_ICONS = { system: Globe, team: Users, - company: Building2, + company: Briefcase, } as const export default function BookingTemplatePicker({ onApply, entityType, defaultAmount }: Props) { diff --git a/components/common/CommandPalette.tsx b/components/common/CommandPalette.tsx index 359528b3..41155bd6 100644 --- a/components/common/CommandPalette.tsx +++ b/components/common/CommandPalette.tsx @@ -8,7 +8,6 @@ import { ArrowLeftRight, Users, Wallet, - Building2, BookOpen, ListTree, BarChart3, @@ -24,6 +23,7 @@ import { ArrowRight, Scale, type LucideIcon, + Truck, } from 'lucide-react' import { cn } from '@/lib/utils' import { useCompany } from '@/contexts/CompanyContext' @@ -49,7 +49,7 @@ const ACTION_ENTRIES: Entry[] = [ const PAGE_ENTRIES: Entry[] = [ { id: 'kunder', label: 'Kunder', icon: Users, href: '/customers' }, - { id: 'leverantörer', label: 'Leverantörer', icon: Building2, href: '/suppliers' }, + { id: 'leverantörer', label: 'Leverantörer', icon: Truck, href: '/suppliers' }, { id: 'kundfakturor', label: 'Kundfakturor', icon: ReceiptText, href: '/invoices', keywords: 'fakturor fakturering invoices kundfaktura' }, { id: 'leverantörsfakturor', label: 'Leverantörsfakturor', icon: Wallet, href: '/supplier-invoices' }, { id: 'bokföring', label: 'Bokföring', icon: BookOpen, href: '/bookkeeping', keywords: 'verifikat journal ledger' }, diff --git a/components/dashboard/DashboardNav.tsx b/components/dashboard/DashboardNav.tsx index c7bc9541..1a340f96 100644 --- a/components/dashboard/DashboardNav.tsx +++ b/components/dashboard/DashboardNav.tsx @@ -22,7 +22,6 @@ import { Menu, X, HelpCircle, - Building2, Wallet, TrendingUp, ClipboardCheck, @@ -51,6 +50,7 @@ import { ShoppingCart, Car, ClipboardList, + Truck, } from 'lucide-react' import { getBranding } from '@/lib/branding/service' import { BrandHomeLink } from '@/components/branding/BrandHomeLink' @@ -256,7 +256,7 @@ const navItems: NavItem[] = [ // row. Anställda is a register (you edit an employee rarely, you run // payroll monthly), so it lives here while Löner stays in Arbeta. { href: '/customers', labelKey: 'customers', icon: Users, group: 'data', fold: 'register' }, - { href: '/suppliers', labelKey: 'suppliers', icon: Building2, group: 'data', fold: 'register' }, + { href: '/suppliers', labelKey: 'suppliers', icon: Truck, group: 'data', fold: 'register' }, { href: '/articles', labelKey: 'articles', icon: Tag, group: 'data', fold: 'register' }, { href: '/salary/employees', labelKey: 'employees', icon: Users, group: 'data', fold: 'register', employerOnly: true }, { href: '/assets', labelKey: 'assets', icon: Package, group: 'data', fold: 'register' }, diff --git a/components/dashboard/HomeDomainSignpost.tsx b/components/dashboard/HomeDomainSignpost.tsx index 2726bec8..3913f1e5 100644 --- a/components/dashboard/HomeDomainSignpost.tsx +++ b/components/dashboard/HomeDomainSignpost.tsx @@ -2,7 +2,7 @@ import { useState } from 'react' import { useTranslations } from 'next-intl' -import { ArrowUpRight, Building2, Loader2 } from 'lucide-react' +import { ArrowUpRight, Loader2, Briefcase } from 'lucide-react' import { performCompanySwitch } from '@/lib/company/switch-client' import { useToast } from '@/components/ui/use-toast' @@ -39,7 +39,7 @@ export default function HomeDomainSignpost({
- +

{t('title')} diff --git a/components/extensions/general/TicWorkspace.tsx b/components/extensions/general/TicWorkspace.tsx index 8b77a28b..f10d7f69 100644 --- a/components/extensions/general/TicWorkspace.tsx +++ b/components/extensions/general/TicWorkspace.tsx @@ -17,7 +17,6 @@ import { import { useExtensionData } from '@/lib/extensions/use-extension-data' import { useToast } from '@/components/ui/use-toast' import { - Building2, CheckCircle, XCircle, MapPin, @@ -29,6 +28,7 @@ import { ShieldCheck, Users, Receipt, + Briefcase, } from 'lucide-react' import { Badge } from '@/components/ui/badge' import Link from 'next/link' @@ -330,7 +330,7 @@ export default function TicWorkspace({ userId }: WorkspaceComponentProps) { - + {/* data-ph-mask: the looked-up company name is user data */} {profile.companyName} diff --git a/components/import/BankFileUploadStep.tsx b/components/import/BankFileUploadStep.tsx index 4208224c..439f26f9 100644 --- a/components/import/BankFileUploadStep.tsx +++ b/components/import/BankFileUploadStep.tsx @@ -18,8 +18,8 @@ import { FileText, AlertCircle, CheckCircle, - Building2, HelpCircle, + Landmark, } from 'lucide-react' import type { BankFileFormatId } from '@/lib/import/bank-file/types' @@ -187,7 +187,7 @@ export default function BankFileUploadStep({ {(selectedFile.size / 1024).toFixed(1)} KB

- + {detectedFormat === 'wise_statement' ? t('bank_format_wise_statement') : detectedFormatName || FORMAT_NAMES[detectedFormat] || detectedFormat} diff --git a/components/import/SIEPreviewStep.tsx b/components/import/SIEPreviewStep.tsx index 5497e092..f0322aac 100644 --- a/components/import/SIEPreviewStep.tsx +++ b/components/import/SIEPreviewStep.tsx @@ -7,7 +7,6 @@ import { Button } from '@/components/ui/button' import { Checkbox } from '@/components/ui/checkbox' import { formatCurrency } from '@/lib/utils' import { - Building2, Calendar, FileText, CheckCircle, @@ -16,6 +15,7 @@ import { ArrowRight, BarChart3, Info, + Briefcase, } from 'lucide-react' import type { ImportPreview, ParseIssue } from '@/lib/import/types' @@ -69,7 +69,7 @@ export default function SIEPreviewStep({ - + Företagsinformation Information från SIE-filen diff --git a/components/settings/ActiveCompanyBadge.tsx b/components/settings/ActiveCompanyBadge.tsx index 170dd8ac..0b178c43 100644 --- a/components/settings/ActiveCompanyBadge.tsx +++ b/components/settings/ActiveCompanyBadge.tsx @@ -1,6 +1,6 @@ 'use client' -import { Building2 } from 'lucide-react' +import { Briefcase } from 'lucide-react' import { useTranslations } from 'next-intl' import { cn } from '@/lib/utils' import { useCompany } from '@/contexts/CompanyContext' @@ -33,7 +33,7 @@ export function ActiveCompanyBadge({ className }: { className?: string }) { )} title={`${t('active_company')}: ${company.name}`} > - diff --git a/components/transactions/TemplatePicker.tsx b/components/transactions/TemplatePicker.tsx index 0922f4fb..3a51239d 100644 --- a/components/transactions/TemplatePicker.tsx +++ b/components/transactions/TemplatePicker.tsx @@ -5,7 +5,7 @@ import { useTranslations } from 'next-intl' import { Input } from '@/components/ui/input' import { Badge } from '@/components/ui/badge' import { Button } from '@/components/ui/button' -import { Search, ChevronDown, ChevronUp, AlertTriangle, Info, Building2, PenLine } from 'lucide-react' +import { Search, ChevronDown, ChevronUp, AlertTriangle, Info, PenLine, Briefcase } from 'lucide-react' import { getCommonTemplates, getAdvancedTemplates, @@ -519,7 +519,7 @@ export default function TemplatePicker({ {sortedLibraryRaw.length > 0 && (

- + {t('my_templates')}

diff --git a/extensions/general/tic/manifest.json b/extensions/general/tic/manifest.json index aaacc478..16c24343 100644 --- a/extensions/general/tic/manifest.json +++ b/extensions/general/tic/manifest.json @@ -10,7 +10,7 @@ "definition": { "name": "Bolagsuppgifter", "category": "import", - "icon": "Building2", + "icon": "Briefcase", "dataPattern": "manual", "hasOwnData": true, "description": "Hämta företagsinformation automatiskt vid registrering", @@ -18,7 +18,7 @@ "quickAction": { "label": "Företagsprofil", "description": "Visa offentliga uppgifter", - "icon": "Building2", + "icon": "Briefcase", "href": "/e/general/tic", "order": 10 } diff --git a/lib/extensions/_generated/sector-definitions.ts b/lib/extensions/_generated/sector-definitions.ts index 83f03d89..4c32e5e6 100644 --- a/lib/extensions/_generated/sector-definitions.ts +++ b/lib/extensions/_generated/sector-definitions.ts @@ -60,7 +60,7 @@ export const EXTENSION_DEFINITIONS: Record = { "name": "Bolagsuppgifter", "sector": "general", "category": "import", - "icon": "Building2", + "icon": "Briefcase", "dataPattern": "manual", "description": "Hämta företagsinformation automatiskt vid registrering", "longDescription": "Fyll i företagsuppgifter automatiskt genom att ange organisationsnummer. Hämtar adress, momsregistrering, F-skattestatus och bankuppgifter från offentliga register via TIC.", @@ -68,7 +68,7 @@ export const EXTENSION_DEFINITIONS: Record = { "quickAction": { "label": "Företagsprofil", "description": "Visa offentliga uppgifter", - "icon": "Building2", + "icon": "Briefcase", "href": "/e/general/tic", "order": 10 } diff --git a/lib/extensions/icon-resolver.tsx b/lib/extensions/icon-resolver.tsx index 9c53af9f..96498043 100644 --- a/lib/extensions/icon-resolver.tsx +++ b/lib/extensions/icon-resolver.tsx @@ -28,7 +28,8 @@ import { Ship, FileText, Shield, - Building2, + Briefcase, + Truck, ArrowRightLeft, Mail, MessageCircle, @@ -65,7 +66,8 @@ const ICON_MAP: Record = { Ship, FileText, Shield, - Building2, + Briefcase, + Truck, ArrowRightLeft, Mail, MessageCircle,