fix: address user feedback — AI loan categorization, invoice UX, email extension, supplier module

- #43: Improve AI categorization to use account 2350 for loan repayments
  instead of incorrectly suggesting 2440 (supplier payables). Add explicit
  prompt guidance distinguishing loans from supplier debts.
- #45: Change unclear invoice unit "mån" to "månad"
- #46: Enable email extension in extensions.config.json so it appears in
  the marketplace and can be activated by users
- #47: Change "Makulera" to "Ta bort utkast" for draft invoices — reserve
  "Makulera" terminology for proforma invoices only
- #48: Show field-level validation errors when supplier creation fails
  instead of generic "Validation failed" message
- #49: Temporarily hide Leverantörer and Leverantörsfakturor from sidebar
  pending module rework

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Jakob Wennberg
2026-03-03 10:56:26 +01:00
co-authored by Claude Opus 4.6
parent 31dec292fe
commit a2ea52954c
11 changed files with 40 additions and 13 deletions
+4 -3
View File
@@ -29,6 +29,7 @@ import {
Bell,
AlertTriangle,
MessageSquare,
Trash2,
} from 'lucide-react'
import type { Invoice, InvoiceItem, Customer, InvoiceStatus, InvoiceReminder, InvoiceDocumentType } from '@/types'
@@ -875,12 +876,12 @@ export default function InvoiceDetailPage({ params }: { params: Promise<{ id: st
)}
<Button
variant="outline"
className="w-full"
className="w-full text-destructive hover:text-destructive"
onClick={() => updateStatus('cancelled')}
disabled={isUpdating}
>
<XCircle className="mr-2 h-4 w-4" />
Makulera
<Trash2 className="mr-2 h-4 w-4" />
Ta bort utkast
</Button>
</>
)}
+1 -1
View File
@@ -49,7 +49,7 @@ const schema = z.object({
type FormData = z.infer<typeof schema>
const currencies: Currency[] = ['SEK', 'EUR', 'USD', 'GBP', 'NOK', 'DKK']
const units = ['st', 'tim', 'dag', 'mån', 'km', 'kg']
const units = ['st', 'tim', 'dag', 'månad', 'km', 'kg']
export default function NewInvoicePage() {
const router = useRouter()
+2 -1
View File
@@ -68,7 +68,8 @@ export default function SupplierDetailPage() {
})
const result = await res.json()
if (!res.ok) {
toast({ title: 'Fel', description: result.error, variant: 'destructive' })
const fieldErrors = result.errors?.map((e: { field: string; message: string }) => `${e.field}: ${e.message}`).join(', ')
toast({ title: 'Fel', description: fieldErrors || result.error || 'Kunde inte uppdatera leverantör', variant: 'destructive' })
} else {
toast({ title: 'Sparat', description: 'Leverantören har uppdaterats' })
setSupplier({ ...result.data, stats: supplier?.stats })
+2 -1
View File
@@ -69,9 +69,10 @@ export default function SuppliersPage() {
const result = await response.json()
if (!response.ok) {
const fieldErrors = result.errors?.map((e: { field: string; message: string }) => `${e.field}: ${e.message}`).join(', ')
toast({
title: 'Fel',
description: result.error || 'Kunde inte skapa leverantör',
description: fieldErrors || result.error || 'Kunde inte skapa leverantör',
variant: 'destructive',
})
} else {
+6 -4
View File
@@ -42,6 +42,7 @@ interface NavItem {
icon: typeof LayoutDashboard
group: string
modes?: EntityType[] // If set, only visible for these entity types. If not set, visible to all.
hidden?: boolean // Temporarily hide from sidebar
}
// All nav items for sidebar and mobile drawer
@@ -50,8 +51,9 @@ const navItems: NavItem[] = [
{ href: '/deadlines', label: 'Deadlines', icon: Calendar, group: 'main' },
{ href: '/invoices', label: 'Fakturor', icon: Receipt, group: 'finans' },
{ href: '/customers', label: 'Kunder', icon: Users, group: 'finans' },
{ href: '/suppliers', label: 'Leverantörer', icon: Building2, group: 'finans' },
{ href: '/supplier-invoices', label: 'Leverantörsfakturor', icon: FileInput, group: 'finans' },
// Temporarily hidden pending module rework (see feedback #49)
{ href: '/suppliers', label: 'Leverantörer', icon: Building2, group: 'finans', hidden: true },
{ href: '/supplier-invoices', label: 'Leverantörsfakturor', icon: FileInput, group: 'finans', hidden: true },
{ href: '/transactions', label: 'Transaktioner', icon: ArrowLeftRight, group: 'finans' },
{ href: '/bookkeeping', label: 'Bokföring', icon: BookOpen, group: 'finans' },
{ href: '/reports', label: 'Rapporter', icon: BarChart3, group: 'finans' },
@@ -131,9 +133,9 @@ export default function DashboardNav({ companyName, entityType, enabledExtension
const closeMobileMenu = () => setIsMobileMenuOpen(false)
// Filter nav items by entity type
// Filter nav items by entity type and hidden flag
const filteredItems = navItems.filter(item =>
!item.modes || item.modes.includes(entityType)
!item.hidden && (!item.modes || item.modes.includes(entityType))
)
const mainItems = filteredItems.filter(i => i.group === 'main')
+1 -1
View File
@@ -1 +1 @@
{"$schema":"./extensions.schema.json","extensions":["enable-banking","ai-categorization","ai-chat"]}
{"$schema":"./extensions.schema.json","extensions":["enable-banking","ai-categorization","ai-chat","email"]}
@@ -110,6 +110,8 @@ function getCategoryAccountMap(entityType: EntityType): Record<string, { account
expense_bank_fees: { account: '6570', label: 'Bankavgifter' },
expense_card_fees: { account: '6570', label: 'Kortavgifter' },
expense_currency_exchange: { account: '7960', label: 'Valutakursförluster' },
expense_interest: { account: '8410', label: 'Räntekostnader' },
financial_loan_repayment: { account: '2350', label: 'Amortering lån (kreditinstitut)' },
expense_other: { account: '6991', label: 'Övriga kostnader' },
}
}
@@ -275,7 +277,8 @@ REGLER:
6. taxCode: "MPI" för avdragsgilla affärskostnader med moms, "MP1" för intäkter med moms, null för momsfria
7. templateId är OBLIGATORISKT — välj alltid den mest passande mallen från listan ovan, även för alternativa förslag
8. isPrivate ska ALLTID vara false — användaren avgör själv vad som är privat
9. Ange TVÅ förslag per transaktion: ett primärt (mest troligt) och ett alternativt (näst mest troligt, annan kategori, lägre confidence). Båda ska vara affärskategorier.`
9. Ange TVÅ förslag per transaktion: ett primärt (mest troligt) och ett alternativt (näst mest troligt, annan kategori, lägre confidence). Båda ska vara affärskategorier.
10. SKULDER: Konto 2440 (leverantörsskulder) ska BARA användas för leverantörsfakturor. Lån/amorteringar från banker eller kreditinstitut (Almi, Nordea, SEB, Handelsbanken, Swedbank, etc.) ska använda 2350 (skulder till kreditinstitut) via mallen financial_loan_repayment. Räntebetalningar ska använda 8410 (räntekostnader).`
const historyContext =
context.recentHistory.length > 0
@@ -106,7 +106,8 @@ VANLIGA BAS-KONTON:
Utgifter: 5010 Lokalhyra | 5410 Forbrukningsinventarier | 5420 Programvara | 5460 Forbrukningsvaror | 5611 Bil/drivmedel | 5800 Resekostnader | 5910 Annonsering | 6071 Representation mat | 6200 Telefon/internet | 6530 Redovisning/konsult | 6570 Bankavgifter | 6991 Ovriga kostnader | ${entityType === 'aktiebolag' ? '7610' : '6991'} Utbildning
Intakter: 3001 Forsaljning 25% | 3002 Forsaljning 12% | 3003 Forsaljning 6% | 3305 Export | 3308 EU-tjanster | 3900 Ovriga intakter
Moms: 2611 Utg moms 25% | 2621 Utg moms 12% | 2631 Utg moms 6% | 2641 Ing moms | 2645 Beraknad ing moms
Ovrigt: 1510 Kundfordringar | 1930 Foretagskonto | 2440 Leverantorsskulder | ${privateAccount} Privat
Skulder: 2350 Skulder till kreditinstitut (banklan, Almi) | 2440 Leverantorsskulder (ENBART for leverantorsfakturor)
Ovrigt: 1510 Kundfordringar | 1930 Foretagskonto | 8410 Rantekostnader | ${privateAccount} Privat
MOMSREGLER:
- standard_25: Normala varor/tjanster (25%)
@@ -4,4 +4,5 @@ export const ENABLED_EXTENSION_IDS: ReadonlySet<string> = new Set([
'enable-banking',
'ai-categorization',
'ai-chat',
'email',
])
@@ -3,9 +3,11 @@ import type { Extension } from '../types'
import { enableBankingExtension } from '@/extensions/general/enable-banking'
import { aiCategorizationExtension } from '@/extensions/general/ai-categorization'
import { aiChatExtension } from '@/extensions/general/ai-chat'
import { emailExtension } from '@/extensions/general/email'
export const FIRST_PARTY_EXTENSIONS: Extension[] = [
enableBankingExtension,
aiCategorizationExtension,
aiChatExtension,
emailExtension,
]
@@ -57,5 +57,20 @@ export const EXTENSION_DEFINITIONS: Record<string, ExtensionDefinition[]> = {
"event": "open-ai-chat"
}
},
{
"slug": "email",
"name": "E-post (Resend)",
"sector": "general",
"category": "operations",
"icon": "Mail",
"dataPattern": "core",
"description": "Skicka fakturor och påminnelser via e-post",
"longDescription": "Aktiverar e-postfunktioner: skicka fakturor till kunder, automatiska betalningspåminnelser (15/30/45 dagar), och e-postmeddelanden. Kräver ett Resend-konto med verifierad domän.",
"readsCoreTables": [
"invoices",
"customers",
"company_settings"
]
},
],
}