From 091d043c850fe5221be6d5ed0091bc405cf7f394 Mon Sep 17 00:00:00 2001 From: Jakob Wennberg Date: Thu, 5 Mar 2026 23:05:49 +0100 Subject: [PATCH] feat: UI polish, lint fixes, onboarding redesign, help page expansion, and test improvements Broad update across dashboard pages, components, extensions, and lib code. Includes ESLint config additions, onboarding flow redesign, settings page refactor, help page content expansion, dead code removal, and test mock fixes. Adds dev docs and public assets. Co-Authored-By: Claude Opus 4.6 --- .claude/skills/create-extension/SKILL.md | 2 +- .claude/skills/erp-api-route/SKILL.md | 2 +- .claude/skills/supabase-migration/SKILL.md | 2 +- .claude/skills/swedish-bookkeeping/SKILL.md | 2 +- .../references/bas-accounts.md | 2 +- CLAUDE.md | 6 +- CONTRIBUTING.md | 4 +- README.md | 10 +- SECURITY.md | 4 +- app/(auth)/login/page.tsx | 12 +- app/(dashboard)/customers/page.tsx | 10 +- app/(dashboard)/expenses/[id]/page.tsx | 13 +- app/(dashboard)/expenses/page.tsx | 8 +- .../extensions/[sector]/[extension]/page.tsx | 4 +- app/(dashboard)/extensions/[sector]/page.tsx | 4 +- app/(dashboard)/help/page.tsx | 41 ++- app/(dashboard)/import/page.tsx | 4 +- app/(dashboard)/invoices/[id]/page.tsx | 2 - app/(dashboard)/invoices/new/page.tsx | 6 +- app/(dashboard)/invoices/page.tsx | 12 +- app/(dashboard)/page.tsx | 7 +- app/(dashboard)/reports/page.tsx | 10 +- app/(dashboard)/settings/page.tsx | 92 +++---- .../supplier-invoices/[id]/page.tsx | 8 +- app/(dashboard)/supplier-invoices/page.tsx | 8 +- app/(dashboard)/suppliers/[id]/page.tsx | 10 +- app/(dashboard)/suppliers/page.tsx | 8 +- app/(onboarding)/onboarding/page.tsx | 157 +++++++---- app/api/ai-consent/__tests__/route.test.ts | 1 + app/api/audit-trail/__tests__/route.test.ts | 3 + app/api/deadlines/[id]/route.ts | 2 +- app/api/import/bank-file/execute/route.ts | 2 +- .../[id]/mark-paid/__tests__/route.test.ts | 2 +- app/api/invoices/route.ts | 2 +- .../audit-trail/__tests__/route.test.ts | 5 + .../[id]/match-supplier-invoice/route.ts | 36 --- .../transactions/suggest-categories/route.ts | 2 +- app/page.tsx | 2 +- components/bookkeeping/ChartOfAccounts.tsx | 8 +- components/bookkeeping/JournalEntryForm.tsx | 10 +- components/bookkeeping/JournalEntryList.tsx | 8 +- components/chat/ChatPanel.tsx | 4 +- .../chat/artifacts/ChatAgingBuckets.tsx | 2 - components/chat/artifacts/ChatDataTable.tsx | 2 +- components/extensions/ExtensionCard.tsx | 1 + .../extensions/ExtensionWorkspaceLoader.tsx | 1 + .../extensions/ExtensionWorkspaceShell.tsx | 1 + components/extensions/SectorCard.tsx | 1 + components/import/BankFileResultStep.tsx | 1 - components/import/BankFileUploadStep.tsx | 1 - components/onboarding/Step1EntityType.tsx | 11 +- components/onboarding/Step2CompanyDetails.tsx | 7 - .../onboarding/Step3TaxRegistration.tsx | 7 - components/onboarding/Step4PreliminaryTax.tsx | 7 - components/onboarding/Step6ConnectBank.tsx | 8 - components/ui/input.tsx | 3 +- components/ui/textarea.tsx | 3 +- dev_docs/04-arkivplan-mall.md | 146 ++++++++++ dev_docs/05-systemdokumentation-mall.md | 259 ++++++++++++++++++ eslint.config.mjs | 9 + extensions.md | 4 +- extensions/general/ai-categorization/index.ts | 3 +- extensions/general/ai-chat/api-routes.ts | 2 +- extensions/general/ai-chat/chatbot/chain.ts | 3 +- extensions/general/ai-chat/chatbot/tools.ts | 2 +- .../general/ai-chat/ingestion/ingest.ts | 2 +- .../calendar/components/CalendarDayCell.tsx | 2 +- .../calendar/components/CalendarWeekView.tsx | 2 +- .../calendar/components/DayDetailModal.tsx | 2 +- .../components/ReceiptDashboard.tsx | 2 +- .../components/TransactionMatcher.tsx | 4 +- .../receipt-ocr/lib/receipt-categorizer.ts | 2 +- .../receipt-ocr/lib/receipt-pipeline.ts | 2 +- .../receipt-ocr/pages/ReceiptsPage.tsx | 2 +- lib/ai/document-analyzer.ts | 2 +- lib/api/schemas.ts | 3 - lib/bookkeeping/account-descriptions.ts | 4 +- lib/bookkeeping/currency-revaluation.ts | 2 - lib/bookkeeping/invoice-entries.ts | 2 +- lib/bookkeeping/mapping-engine.ts | 3 - lib/bookkeeping/transaction-entries.ts | 2 +- lib/core/documents/document-service.ts | 2 +- .../__tests__/document-matcher.test.ts | 2 +- lib/extensions/__tests__/ai-consent.test.ts | 1 + lib/extensions/__tests__/sectors.test.ts | 3 +- lib/import/sie-import.ts | 2 +- lib/reports/__tests__/ar-ledger.test.ts | 1 + .../__tests__/ar-reconciliation.test.ts | 1 + lib/reports/__tests__/balance-sheet.test.ts | 1 + .../__tests__/full-archive-export.test.ts | 1 + lib/reports/__tests__/general-ledger.test.ts | 1 + .../__tests__/income-statement.test.ts | 1 + .../__tests__/journal-register.test.ts | 1 + lib/reports/__tests__/sie-export.test.ts | 1 + lib/reports/__tests__/supplier-ledger.test.ts | 1 + .../__tests__/supplier-reconciliation.test.ts | 1 + lib/reports/__tests__/trial-balance.test.ts | 1 + lib/reports/__tests__/vat-declaration.test.ts | 1 + .../ink2/__tests__/ink2-engine.test.ts | 2 +- .../ne-bilaga/__tests__/ne-engine.test.ts | 1 - lib/tax/calculator.ts | 2 - lib/tax/deadline-config.ts | 7 +- lib/tax/deadline-generator.ts | 5 +- public/docs/arkivplan-mall.md | 146 ++++++++++ public/docs/systemdokumentation-mall.md | 259 ++++++++++++++++++ public/gnubokiceon-removebg-preview.png | Bin 0 -> 108424 bytes 106 files changed, 1177 insertions(+), 326 deletions(-) create mode 100644 dev_docs/04-arkivplan-mall.md create mode 100644 dev_docs/05-systemdokumentation-mall.md create mode 100644 public/docs/arkivplan-mall.md create mode 100644 public/docs/systemdokumentation-mall.md create mode 100644 public/gnubokiceon-removebg-preview.png diff --git a/.claude/skills/create-extension/SKILL.md b/.claude/skills/create-extension/SKILL.md index 784abd94..40130aff 100644 --- a/.claude/skills/create-extension/SKILL.md +++ b/.claude/skills/create-extension/SKILL.md @@ -1,6 +1,6 @@ --- name: create-extension -description: "Generate and implement extensions for erp-base: scaffold files, configure manifests, write event handlers, API routes, services, workspace UIs, settings panels, and testing. Use when creating new extensions, adding surfaces to existing extensions, or understanding the extension architecture. Covers the full lifecycle from scaffolding to registration." +description: "Generate and implement extensions for gnubok: scaffold files, configure manifests, write event handlers, API routes, services, workspace UIs, settings panels, and testing. Use when creating new extensions, adding surfaces to existing extensions, or understanding the extension architecture. Covers the full lifecycle from scaffolding to registration." --- # Extension Generator diff --git a/.claude/skills/erp-api-route/SKILL.md b/.claude/skills/erp-api-route/SKILL.md index 7afb5469..bca2deaa 100644 --- a/.claude/skills/erp-api-route/SKILL.md +++ b/.claude/skills/erp-api-route/SKILL.md @@ -1,6 +1,6 @@ --- name: erp-api-route -description: "Generate Next.js 16 API routes for erp-base with correct auth guards, Supabase client usage, event emission, journal entry creation, and error handling. Use when creating new API endpoints in app/api/. Handles the Next.js 16 async params pattern, ensureInitialized() for events, non-blocking journal entry wrapping, and defense-in-depth user_id filtering." +description: "Generate Next.js 16 API routes for gnubok with correct auth guards, Supabase client usage, event emission, journal entry creation, and error handling. Use when creating new API endpoints in app/api/. Handles the Next.js 16 async params pattern, ensureInitialized() for events, non-blocking journal entry wrapping, and defense-in-depth user_id filtering." --- # ERP API Route Generator diff --git a/.claude/skills/supabase-migration/SKILL.md b/.claude/skills/supabase-migration/SKILL.md index 29ec0023..ce6c67b3 100644 --- a/.claude/skills/supabase-migration/SKILL.md +++ b/.claude/skills/supabase-migration/SKILL.md @@ -1,6 +1,6 @@ --- name: supabase-migration -description: "Generate Supabase database migrations for the erp-base project with correct RLS policies, triggers, indexes, and Swedish accounting constraints. Use when creating new tables, adding columns, modifying constraints (e.g. source_type CHECK), or any DDL operation on the Supabase database. Ensures legal compliance with BFL 7-year retention, immutability triggers, and period lock enforcement." +description: "Generate Supabase database migrations for the gnubok project with correct RLS policies, triggers, indexes, and Swedish accounting constraints. Use when creating new tables, adding columns, modifying constraints (e.g. source_type CHECK), or any DDL operation on the Supabase database. Ensures legal compliance with BFL 7-year retention, immutability triggers, and period lock enforcement." --- # Supabase Migration Generator diff --git a/.claude/skills/swedish-bookkeeping/SKILL.md b/.claude/skills/swedish-bookkeeping/SKILL.md index e12ecb5f..fe6bda2a 100644 --- a/.claude/skills/swedish-bookkeeping/SKILL.md +++ b/.claude/skills/swedish-bookkeeping/SKILL.md @@ -1,6 +1,6 @@ --- name: swedish-bookkeeping -description: "Swedish double-entry bookkeeping domain knowledge for erp-base: BAS account codes, VAT treatments, journal entry patterns, entity type differences (enskild firma vs aktiebolag), and legal constraints (BFL/BFN). Use when creating journal entry generators, modifying bookkeeping logic, adding VAT handling, working with reports, or any accounting-related code. Prevents illegal accounting operations and ensures correct account/VAT mappings." +description: "Swedish double-entry bookkeeping domain knowledge for gnubok: BAS account codes, VAT treatments, journal entry patterns, entity type differences (enskild firma vs aktiebolag), and legal constraints (BFL/BFN). Use when creating journal entry generators, modifying bookkeeping logic, adding VAT handling, working with reports, or any accounting-related code. Prevents illegal accounting operations and ensures correct account/VAT mappings." --- # Swedish Bookkeeping Reference diff --git a/.claude/skills/swedish-bookkeeping/references/bas-accounts.md b/.claude/skills/swedish-bookkeeping/references/bas-accounts.md index 9fd2a159..102628f8 100644 --- a/.claude/skills/swedish-bookkeeping/references/bas-accounts.md +++ b/.claude/skills/swedish-bookkeeping/references/bas-accounts.md @@ -1,4 +1,4 @@ -# BAS Account Quick Reference — erp-base +# BAS Account Quick Reference — gnubok Accounts used in codebase. Search `dev_docs/BASKONTOPLAN.md` for full chart. diff --git a/CLAUDE.md b/CLAUDE.md index 49ed9f57..eecbbc99 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -1,8 +1,8 @@ -# CLAUDE.md — erp-base +# CLAUDE.md — gnubok ## Project Overview -erp-base is a Swedish-focused accounting SaaS for sole traders (enskild firma) and limited companies (aktiebolag). It implements double-entry bookkeeping compliant with Swedish accounting law (Bokforingslagen), including VAT handling, tax reporting, and 7-year document retention. +gnubok is a Swedish-focused accounting SaaS for sole traders (enskild firma) and limited companies (aktiebolag). It implements double-entry bookkeeping compliant with Swedish accounting law (Bokforingslagen), including VAT handling, tax reporting, and 7-year document retention. **Tech stack**: Next.js 16 (App Router), React 19, TypeScript (strict), Supabase (PostgreSQL + RLS + magic link auth), Tailwind CSS 4 + shadcn/ui, Vercel hosting. @@ -363,4 +363,4 @@ Hosted on **Vercel**. Cron jobs in `vercel.json` (banking sync daily 05:00, dead **Core env vars**: `NEXT_PUBLIC_SUPABASE_URL`, `NEXT_PUBLIC_SUPABASE_ANON_KEY`, `SUPABASE_SERVICE_ROLE_KEY`, `NEXT_PUBLIC_APP_URL`, `CRON_SECRET`. Extension env vars only needed when that extension is enabled. ## Other -Never create a NUL/nul file: \erp-base\NUL +Never create a NUL/nul file: \gnubok\NUL diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 30916ec8..b42eb707 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1,6 +1,6 @@ -# Contributing to erp-base +# Contributing to gnubok -Thank you for your interest in contributing to erp-base. This guide covers the development workflow, coding standards, and submission process. +Thank you for your interest in contributing to gnubok. This guide covers the development workflow, coding standards, and submission process. ## Getting Started diff --git a/README.md b/README.md index c70e529f..51fc18e5 100644 --- a/README.md +++ b/README.md @@ -1,12 +1,12 @@ -# erp-base +# gnubok Open-source Swedish accounting software for sole traders (enskild firma) and limited companies (aktiebolag). [![License: AGPL-3.0-or-later](https://img.shields.io/badge/License-AGPL--3.0--or--later-blue.svg)](LICENSE) -## What is erp-base? +## What is gnubok? -erp-base implements double-entry bookkeeping compliant with Swedish accounting law (Bokforingslagen). It supports the BAS 2026 chart of accounts, handles VAT declarations (momsdeklaration), SIE import/export, and enforces 7-year document retention. Built for sole traders and limited companies operating in Sweden. +gnubok implements double-entry bookkeeping compliant with Swedish accounting law (Bokforingslagen). It supports the BAS 2026 chart of accounts, handles VAT declarations (momsdeklaration), SIE import/export, and enforces 7-year document retention. Built for sole traders and limited companies operating in Sweden. ## Features @@ -24,8 +24,8 @@ erp-base implements double-entry bookkeeping compliant with Swedish accounting l ```bash # Clone and configure -git clone https://github.com/erp-mafia/erp-base.git -cd erp-base +git clone https://github.com/erp-mafia/gnubok.git +cd gnubok cp .env.example .env # Fill in your Supabase credentials # Start with Docker Compose diff --git a/SECURITY.md b/SECURITY.md index dab5f2e0..8c4d3b6d 100644 --- a/SECURITY.md +++ b/SECURITY.md @@ -2,9 +2,9 @@ ## Reporting Vulnerabilities -If you discover a security vulnerability in erp-base, please report it responsibly. **Do not open a public issue.** +If you discover a security vulnerability in gnubok, please report it responsibly. **Do not open a public issue.** -Email: **security@gnubok.se** +Email: **jakob.wennberg@arcim.io** Include: - Description of the vulnerability diff --git a/app/(auth)/login/page.tsx b/app/(auth)/login/page.tsx index 3255469f..b7121f86 100644 --- a/app/(auth)/login/page.tsx +++ b/app/(auth)/login/page.tsx @@ -7,6 +7,7 @@ import { Input } from '@/components/ui/input' import { Label } from '@/components/ui/label' import { useToast } from '@/components/ui/use-toast' import { Loader2, Mail, ArrowLeft } from 'lucide-react' +import Image from 'next/image' import { getErrorMessage } from '@/lib/errors/get-error-message' export default function LoginPage() { @@ -100,9 +101,14 @@ export default function LoginPage() {
-

- Gnubok -

+ Gnubok

Logga in med din e-post för att hantera din ekonomi

diff --git a/app/(dashboard)/customers/page.tsx b/app/(dashboard)/customers/page.tsx index 7a09575d..18978a92 100644 --- a/app/(dashboard)/customers/page.tsx +++ b/app/(dashboard)/customers/page.tsx @@ -47,10 +47,6 @@ export default function CustomersPage() { const { toast } = useToast() const supabase = createClient() - useEffect(() => { - fetchCustomers() - }, []) - async function fetchCustomers() { setIsLoading(true) const { data, error } = await supabase @@ -70,6 +66,10 @@ export default function CustomersPage() { setIsLoading(false) } + useEffect(() => { + fetchCustomers() + }, []) + async function handleCreateCustomer(data: CreateCustomerInput) { setIsCreating(true) @@ -167,7 +167,7 @@ export default function CustomersPage() {

Inga träffar

- Inga kunder matchar "{searchTerm}" + Inga kunder matchar "{searchTerm}"

) : ( diff --git a/app/(dashboard)/expenses/[id]/page.tsx b/app/(dashboard)/expenses/[id]/page.tsx index e37efe9a..e5262710 100644 --- a/app/(dashboard)/expenses/[id]/page.tsx +++ b/app/(dashboard)/expenses/[id]/page.tsx @@ -13,7 +13,6 @@ import { ArrowLeft, CheckCircle, CreditCard, FileText, Trash2 } from 'lucide-rea import Link from 'next/link' import { AccountNumber } from '@/components/ui/account-number' import { DestructiveConfirmDialog, useDestructiveConfirm } from '@/components/ui/destructive-confirm-dialog' -import { formatCurrency } from '@/lib/utils' import type { SupplierInvoice, SupplierInvoiceItem, SupplierInvoicePayment, EntityType } from '@/types' const statusConfig: Record = { @@ -35,18 +34,13 @@ export default function ExpenseDetailPage() { const router = useRouter() const { toast } = useToast() const [invoice, setInvoice] = useState(null) - const [entityType, setEntityType] = useState('enskild_firma') + const [, setEntityType] = useState('enskild_firma') const [isLoading, setIsLoading] = useState(true) const [isPayDialogOpen, setIsPayDialogOpen] = useState(false) const [payAmount, setPayAmount] = useState('') const [isProcessing, setIsProcessing] = useState(false) const { dialogProps: confirmDialogProps, confirm: confirmAction } = useDestructiveConfirm() - useEffect(() => { - fetchInvoice() - fetchEntityType() - }, [params.id]) - async function fetchInvoice() { setIsLoading(true) const res = await fetch(`/api/supplier-invoices/${params.id}`) @@ -72,6 +66,11 @@ export default function ExpenseDetailPage() { } } + useEffect(() => { + fetchInvoice() + fetchEntityType() + }, [params.id]) + async function handleApprove() { setIsProcessing(true) const res = await fetch(`/api/supplier-invoices/${params.id}/approve`, { method: 'POST' }) diff --git a/app/(dashboard)/expenses/page.tsx b/app/(dashboard)/expenses/page.tsx index b99217bb..8b1ef7b1 100644 --- a/app/(dashboard)/expenses/page.tsx +++ b/app/(dashboard)/expenses/page.tsx @@ -67,10 +67,6 @@ export default function ExpensesPage() { const { toast } = useToast() const supabase = createClient() - useEffect(() => { - fetchExpenses() - }, []) - async function fetchExpenses() { setIsLoading(true) const { data, error } = await supabase @@ -86,6 +82,10 @@ export default function ExpensesPage() { setIsLoading(false) } + useEffect(() => { + fetchExpenses() + }, []) + const filteredInvoices = invoices.filter((inv) => { const matchesSearch = (inv.supplier?.name || '').toLowerCase().includes(searchTerm.toLowerCase()) || diff --git a/app/(dashboard)/extensions/[sector]/[extension]/page.tsx b/app/(dashboard)/extensions/[sector]/[extension]/page.tsx index 17737ee2..0c0de738 100644 --- a/app/(dashboard)/extensions/[sector]/[extension]/page.tsx +++ b/app/(dashboard)/extensions/[sector]/[extension]/page.tsx @@ -1,6 +1,7 @@ import { notFound } from 'next/navigation' import { getExtensionDefinition, getSector } from '@/lib/extensions/sectors' import { resolveIcon } from '@/lib/extensions/icon-resolver' +import type { SectorSlug } from '@/lib/extensions/types' import CategoryBadge from '@/components/extensions/CategoryBadge' import ExtensionToggleButton from '@/components/extensions/ExtensionToggleButton' import Link from 'next/link' @@ -15,7 +16,8 @@ export default async function ExtensionDetailPage({ const definition = getExtensionDefinition(sectorSlug, extensionSlug) if (!definition) notFound() - const sector = getSector(sectorSlug as any) + const sector = getSector(sectorSlug as SectorSlug) + const Icon = resolveIcon(definition.icon) const dataPatternLabels: Record = { diff --git a/app/(dashboard)/extensions/[sector]/page.tsx b/app/(dashboard)/extensions/[sector]/page.tsx index 64729386..9ebe39dd 100644 --- a/app/(dashboard)/extensions/[sector]/page.tsx +++ b/app/(dashboard)/extensions/[sector]/page.tsx @@ -1,6 +1,7 @@ import { notFound } from 'next/navigation' import { getSector } from '@/lib/extensions/sectors' import { resolveIcon } from '@/lib/extensions/icon-resolver' +import type { SectorSlug } from '@/lib/extensions/types' import ExtensionCard from '@/components/extensions/ExtensionCard' import Link from 'next/link' @@ -10,10 +11,11 @@ export default async function SectorExtensionsPage({ params: Promise<{ sector: string }> }) { const { sector: sectorSlug } = await params - const sector = getSector(sectorSlug as any) + const sector = getSector(sectorSlug as SectorSlug) if (!sector) notFound() + const Icon = resolveIcon(sector.icon) return ( diff --git a/app/(dashboard)/help/page.tsx b/app/(dashboard)/help/page.tsx index 61ca1358..ad9e1406 100644 --- a/app/(dashboard)/help/page.tsx +++ b/app/(dashboard)/help/page.tsx @@ -12,6 +12,7 @@ import { Calculator, Building2, FileText, + FileDown, ExternalLink, ChevronDown, ChevronUp, @@ -372,7 +373,7 @@ export default function HelpPage() {

- Inga termer hittades för "{searchQuery}" + Inga termer hittades för "{searchQuery}"

@@ -388,6 +389,44 @@ export default function HelpPage() { )}
+ {/* Document templates */} + + + Dokument & Mallar + Lagstadgade mallar för din bokföring — ladda ner, fyll i och spara + + + + + + {/* External resources */} diff --git a/app/(dashboard)/import/page.tsx b/app/(dashboard)/import/page.tsx index 68d4e104..efcc3b72 100644 --- a/app/(dashboard)/import/page.tsx +++ b/app/(dashboard)/import/page.tsx @@ -311,13 +311,13 @@ function SIEImportWizard() { const [error, setError] = useState(null) const [file, setFile] = useState(null) - const [_parsed, setParsed] = useState(null) + const [, setParsed] = useState(null) const [mappings, setMappings] = useState([]) const [basAccounts, setBasAccounts] = useState([]) const [preview, setPreview] = useState(null) const [issues, setIssues] = useState([]) const [importResult, setImportResult] = useState(null) - const [_sieAccounts, setSieAccounts] = useState<{ number: string; name: string }[]>([]) + const [, setSieAccounts] = useState<{ number: string; name: string }[]>([]) const [isCreatingAccounts, setIsCreatingAccounts] = useState(false) // Skip the mapping step when all accounts are already mapped diff --git a/app/(dashboard)/invoices/[id]/page.tsx b/app/(dashboard)/invoices/[id]/page.tsx index 79e0f12a..6c38db67 100644 --- a/app/(dashboard)/invoices/[id]/page.tsx +++ b/app/(dashboard)/invoices/[id]/page.tsx @@ -353,8 +353,6 @@ export default function InvoiceDetailPage({ params }: { params: Promise<{ id: st const isProforma = docType === 'proforma' const isDeliveryNote = docType === 'delivery_note' const isRealInvoice = docType === 'invoice' - const docLabel = isProforma ? 'Proformafaktura' : isDeliveryNote ? 'Följesedel' : 'Faktura' - return (
{/* Header */} diff --git a/app/(dashboard)/invoices/new/page.tsx b/app/(dashboard)/invoices/new/page.tsx index 7b8d7f48..0aa94206 100644 --- a/app/(dashboard)/invoices/new/page.tsx +++ b/app/(dashboard)/invoices/new/page.tsx @@ -1,7 +1,7 @@ 'use client' import { useState, useEffect } from 'react' -import { useRouter, useSearchParams } from 'next/navigation' +import { useRouter } from 'next/navigation' import { createClient } from '@/lib/supabase/client' import { useForm, useFieldArray, Controller } from 'react-hook-form' import { zodResolver } from '@hookform/resolvers/zod' @@ -55,8 +55,6 @@ export default function NewInvoicePage() { const router = useRouter() const { toast } = useToast() const supabase = createClient() - const searchParams = useSearchParams() - const preselectedCustomerId = searchParams.get('customer_id') const [customers, setCustomers] = useState([]) const [isLoading, setIsLoading] = useState(true) @@ -68,7 +66,7 @@ export default function NewInvoicePage() { const [showSendPrompt, setShowSendPrompt] = useState(false) const [isSending, setIsSending] = useState(false) const [isPreviewing, setIsPreviewing] = useState(false) - const [defaultNotes, setDefaultNotes] = useState(null) + const [, setDefaultNotes] = useState(null) const [isCreateCustomerOpen, setIsCreateCustomerOpen] = useState(false) const [isCreatingCustomer, setIsCreatingCustomer] = useState(false) diff --git a/app/(dashboard)/invoices/page.tsx b/app/(dashboard)/invoices/page.tsx index f10a5a15..7c3289f1 100644 --- a/app/(dashboard)/invoices/page.tsx +++ b/app/(dashboard)/invoices/page.tsx @@ -7,11 +7,11 @@ import { Button } from '@/components/ui/button' import { Card, CardContent } from '@/components/ui/card' import { Badge } from '@/components/ui/badge' import { Input } from '@/components/ui/input' -import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs' +import { Tabs, TabsList, TabsTrigger } from '@/components/ui/tabs' import { PageHeader } from '@/components/ui/page-header' import { useToast } from '@/components/ui/use-toast' import { formatCurrency, formatDate } from '@/lib/utils' -import { Plus, Search, Receipt, FileText, Send, CheckCircle, Clock, XCircle, ReceiptText, AlertTriangle, FileQuestion, Truck } from 'lucide-react' +import { Plus, Search, Receipt, FileText, Send, CheckCircle, Clock, XCircle, ReceiptText, FileQuestion, Truck } from 'lucide-react' import { EmptyInvoices } from '@/components/ui/empty-state' import type { Invoice, InvoiceStatus } from '@/types' @@ -53,10 +53,6 @@ export default function InvoicesPage() { const { toast } = useToast() const supabase = createClient() - useEffect(() => { - fetchInvoices() - }, []) - async function fetchInvoices() { setIsLoading(true) const { data, error } = await supabase @@ -76,6 +72,10 @@ export default function InvoicesPage() { setIsLoading(false) } + useEffect(() => { + fetchInvoices() + }, []) + const filteredInvoices = invoices.filter((invoice) => { const matchesSearch = invoice.invoice_number.toLowerCase().includes(searchTerm.toLowerCase()) || diff --git a/app/(dashboard)/page.tsx b/app/(dashboard)/page.tsx index 9e56b203..a2977742 100644 --- a/app/(dashboard)/page.tsx +++ b/app/(dashboard)/page.tsx @@ -177,8 +177,9 @@ export default async function DashboardPage() { })) // Fetch upcoming deadlines (next 7 days + overdue) - const today = new Date().toISOString().split('T')[0] - const nextWeek = new Date(Date.now() + 7 * 24 * 60 * 60 * 1000).toISOString().split('T')[0] + const now = new Date() + const today = now.toISOString().split('T')[0] + const nextWeek = new Date(now.getTime() + 7 * 24 * 60 * 60 * 1000).toISOString().split('T')[0] const { data: deadlines } = await supabase .from('deadlines') @@ -258,7 +259,7 @@ export default async function DashboardPage() { recentReceiptActivity.map((r) => new Date(r.created_at).toISOString().split('T')[0]) ) - let checkDate = new Date(todayDate) + const checkDate = new Date(todayDate) while (activityDates.has(checkDate.toISOString().split('T')[0])) { streakCount++ checkDate.setDate(checkDate.getDate() - 1) diff --git a/app/(dashboard)/reports/page.tsx b/app/(dashboard)/reports/page.tsx index 69fd210d..646c464d 100644 --- a/app/(dashboard)/reports/page.tsx +++ b/app/(dashboard)/reports/page.tsx @@ -34,11 +34,6 @@ export default function ReportsPage() { const [activeTab, setActiveTab] = useState('trial-balance') const [entityType, setEntityType] = useState(null) - useEffect(() => { - fetchPeriods() - fetchEntityType() - }, []) - async function fetchPeriods() { const res = await fetch('/api/bookkeeping/fiscal-periods') const { data } = await res.json() @@ -60,6 +55,11 @@ export default function ReportsPage() { } } + useEffect(() => { + fetchPeriods() + fetchEntityType() + }, []) + const isEnskildFirma = entityType === 'enskild_firma' const isAktiebolag = entityType === 'aktiebolag' diff --git a/app/(dashboard)/settings/page.tsx b/app/(dashboard)/settings/page.tsx index 50e66f51..50dec271 100644 --- a/app/(dashboard)/settings/page.tsx +++ b/app/(dashboard)/settings/page.tsx @@ -60,52 +60,6 @@ export default function SettingsPage() { setMounted(true) }, []) - useEffect(() => { - fetchData() - - // Handle callback messages - const bankConnected = searchParams.get('bank_connected') - const bankError = searchParams.get('bank_error') - - if (bankConnected === 'true') { - toast({ - title: 'Bank ansluten!', - description: 'Din bank är nu kopplad. Transaktioner hämtas...', - }) - - // Auto-sync transactions after connection - const connectionId = searchParams.get('connection_id') - if (connectionId) { - fetch('/api/extensions/ext/enable-banking/sync', { - method: 'POST', - headers: { 'Content-Type': 'application/json' }, - body: JSON.stringify({ connection_id: connectionId, days_back: 90 }), - }) - .then(res => res.json()) - .then(data => { - if (data.imported > 0) { - toast({ - title: 'Transaktioner hämtade', - description: `${data.imported} transaktioner importerade`, - }) - } - }) - .catch(() => {}) - } - - router.replace('/settings?tab=banking') - } - - if (bankError) { - toast({ - title: 'Anslutning misslyckades', - description: decodeURIComponent(bankError), - variant: 'destructive', - }) - router.replace('/settings') - } - }, [searchParams]) - async function fetchData() { setIsLoading(true) @@ -152,6 +106,52 @@ export default function SettingsPage() { setIsLoading(false) } + useEffect(() => { + fetchData() + + // Handle callback messages + const bankConnected = searchParams.get('bank_connected') + const bankError = searchParams.get('bank_error') + + if (bankConnected === 'true') { + toast({ + title: 'Bank ansluten!', + description: 'Din bank är nu kopplad. Transaktioner hämtas...', + }) + + // Auto-sync transactions after connection + const connectionId = searchParams.get('connection_id') + if (connectionId) { + fetch('/api/extensions/ext/enable-banking/sync', { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ connection_id: connectionId, days_back: 90 }), + }) + .then(res => res.json()) + .then(data => { + if (data.imported > 0) { + toast({ + title: 'Transaktioner hämtade', + description: `${data.imported} transaktioner importerade`, + }) + } + }) + .catch(() => {}) + } + + router.replace('/settings?tab=banking') + } + + if (bankError) { + toast({ + title: 'Anslutning misslyckades', + description: decodeURIComponent(bankError), + variant: 'destructive', + }) + router.replace('/settings') + } + }, [searchParams]) + async function handleSaveSettings(e: React.FormEvent) { e.preventDefault() if (!settings) return diff --git a/app/(dashboard)/supplier-invoices/[id]/page.tsx b/app/(dashboard)/supplier-invoices/[id]/page.tsx index 5bedbb4c..960c1f33 100644 --- a/app/(dashboard)/supplier-invoices/[id]/page.tsx +++ b/app/(dashboard)/supplier-invoices/[id]/page.tsx @@ -50,10 +50,6 @@ export default function SupplierInvoiceDetailPage() { const [isProcessing, setIsProcessing] = useState(false) const { dialogProps: confirmDialogProps, confirm: confirmAction } = useDestructiveConfirm() - useEffect(() => { - fetchInvoice() - }, [params.id]) - async function fetchInvoice() { setIsLoading(true) const res = await fetch(`/api/supplier-invoices/${params.id}`) @@ -67,6 +63,10 @@ export default function SupplierInvoiceDetailPage() { setIsLoading(false) } + useEffect(() => { + fetchInvoice() + }, [params.id]) + async function handleApprove() { setIsProcessing(true) const res = await fetch(`/api/supplier-invoices/${params.id}/approve`, { method: 'POST' }) diff --git a/app/(dashboard)/supplier-invoices/page.tsx b/app/(dashboard)/supplier-invoices/page.tsx index d922e194..fa721a62 100644 --- a/app/(dashboard)/supplier-invoices/page.tsx +++ b/app/(dashboard)/supplier-invoices/page.tsx @@ -38,10 +38,6 @@ export default function SupplierInvoicesPage() { const [isLoading, setIsLoading] = useState(true) const [activeTab, setActiveTab] = useState('all') - useEffect(() => { - fetchInvoices() - }, []) - async function fetchInvoices() { setIsLoading(true) const res = await fetch('/api/supplier-invoices?status=all') @@ -50,6 +46,10 @@ export default function SupplierInvoicesPage() { setIsLoading(false) } + useEffect(() => { + fetchInvoices() + }, []) + const filteredInvoices = invoices.filter((inv) => { switch (activeTab) { case 'registered': return inv.status === 'registered' diff --git a/app/(dashboard)/suppliers/[id]/page.tsx b/app/(dashboard)/suppliers/[id]/page.tsx index 07b882e5..e2b597fb 100644 --- a/app/(dashboard)/suppliers/[id]/page.tsx +++ b/app/(dashboard)/suppliers/[id]/page.tsx @@ -34,11 +34,6 @@ export default function SupplierDetailPage() { const [isSaving, setIsSaving] = useState(false) const { dialogProps: confirmDialogProps, confirm: confirmAction } = useDestructiveConfirm() - useEffect(() => { - fetchSupplier() - fetchInvoices() - }, [params.id]) - async function fetchSupplier() { setIsLoading(true) const res = await fetch(`/api/suppliers/${params.id}`) @@ -59,6 +54,11 @@ export default function SupplierDetailPage() { } } + useEffect(() => { + fetchSupplier() + fetchInvoices() + }, [params.id]) + async function handleUpdate(data: CreateSupplierInput) { setIsSaving(true) const res = await fetch(`/api/suppliers/${params.id}`, { diff --git a/app/(dashboard)/suppliers/page.tsx b/app/(dashboard)/suppliers/page.tsx index 1012c11f..5a323b88 100644 --- a/app/(dashboard)/suppliers/page.tsx +++ b/app/(dashboard)/suppliers/page.tsx @@ -34,10 +34,6 @@ export default function SuppliersPage() { const { toast } = useToast() const supabase = createClient() - useEffect(() => { - fetchSuppliers() - }, []) - async function fetchSuppliers() { setIsLoading(true) const { data, error } = await supabase @@ -57,6 +53,10 @@ export default function SuppliersPage() { setIsLoading(false) } + useEffect(() => { + fetchSuppliers() + }, []) + async function handleCreateSupplier(data: CreateSupplierInput) { setIsCreating(true) diff --git a/app/(onboarding)/onboarding/page.tsx b/app/(onboarding)/onboarding/page.tsx index 0f8bfe4c..fef89ae2 100644 --- a/app/(onboarding)/onboarding/page.tsx +++ b/app/(onboarding)/onboarding/page.tsx @@ -2,10 +2,11 @@ import { useState, useEffect, Suspense } from 'react' import { useRouter, useSearchParams } from 'next/navigation' +import Image from 'next/image' import { createClient } from '@/lib/supabase/client' -import { Progress } from '@/components/ui/progress' import { useToast } from '@/components/ui/use-toast' import { Loader2 } from 'lucide-react' +import { cn } from '@/lib/utils' import { validatePeriodDuration } from '@/lib/bookkeeping/validate-period-duration' import type { CompanySettings, EntityType, MomsPeriod } from '@/types' @@ -15,12 +16,12 @@ import Step3TaxRegistration from '@/components/onboarding/Step3TaxRegistration' import Step4PreliminaryTax from '@/components/onboarding/Step4PreliminaryTax' import Step5ConnectBank from '@/components/onboarding/Step6ConnectBank' -const STEP_TITLES = [ - 'Verksamhetsform', - 'Företagsuppgifter', - 'Skatteregistrering', - 'F-skatt', - 'Anslut bank', +const STEP_INFO = [ + { title: 'Välkommen', subtitle: 'Välj din företagsform för att komma igång.', label: 'Företagsform' }, + { title: 'Ditt företag', subtitle: 'Uppgifterna visas på fakturor och dokument.', label: 'Uppgifter' }, + { title: 'Skatt & bokföring', subtitle: 'F-skatt, räkenskapsår och momsregistrering.', label: 'Skatt' }, + { title: 'Preliminärskatt', subtitle: 'Frivilligt — hjälper dig hålla koll på skatten.', label: 'F-skatt' }, + { title: 'Bankuppgifter', subtitle: 'Dessa visas på dina fakturor.', label: 'Bank' }, ] function translatePeriodError(msg: string): string { @@ -33,7 +34,11 @@ function translatePeriodError(msg: string): string { export default function OnboardingPage() { return ( -
}> + + +
+ }> ) @@ -51,7 +56,6 @@ function OnboardingPageContent() { const [settings, setSettings] = useState>({}) const totalSteps = 5 - const stepTitles = STEP_TITLES // Load existing settings on mount useEffect(() => { @@ -63,7 +67,7 @@ function OnboardingPageContent() { return } - const { data, error } = await supabase + const { data } = await supabase .from('company_settings') .select('*') .eq('user_id', user.id) @@ -82,26 +86,6 @@ function OnboardingPageContent() { loadSettings() }, [supabase, router, toast]) - // Handle bank_connected callback from PSD2 flow - useEffect(() => { - if (searchParams.get('bank_connected') === 'true') { - toast({ - title: 'Bank ansluten!', - description: 'Din bank har kopplats.', - }) - // Complete onboarding after bank connection - saveSettings({ onboarding_complete: true }, totalSteps).then((success) => { - if (success) { - toast({ - title: 'Välkommen!', - description: 'Din profil är nu redo.', - }) - router.push('/') - } - }) - } - }, [searchParams]) - const saveSettings = async (updates: Partial, nextStep?: number) => { setIsSaving(true) @@ -146,6 +130,26 @@ function OnboardingPageContent() { return true } + // Handle bank_connected callback from PSD2 flow + useEffect(() => { + if (searchParams.get('bank_connected') === 'true') { + toast({ + title: 'Bank ansluten!', + description: 'Din bank har kopplats.', + }) + // Complete onboarding after bank connection + saveSettings({ onboarding_complete: true }, totalSteps).then((success) => { + if (success) { + toast({ + title: 'Välkommen!', + description: 'Din profil är nu redo.', + }) + router.push('/') + } + }) + } + }, [searchParams]) + const handleNext = async (stepData: Partial) => { // Fix org number bug: clear dependent fields when entity type changes if (currentStep === 1 && stepData.entity_type && stepData.entity_type !== settings.entity_type) { @@ -268,7 +272,7 @@ function OnboardingPageContent() { onConflict: 'user_id,period_start,period_end', }) } - } catch (err) { + } catch { toast({ title: 'Kunde inte skapa räkenskapsår', description: 'Ett fel uppstod när räkenskapsåret skulle skapas. Försök igen.', @@ -307,13 +311,13 @@ function OnboardingPageContent() { if (isLoading) { return ( -
- +
+
) } - const progressPercent = ((currentStep - 1) / (totalSteps - 1)) * 100 + const stepInfo = STEP_INFO[currentStep - 1] const renderSteps = () => ( <> @@ -408,26 +412,77 @@ function OnboardingPageContent() { ) return ( -
- {/* Header */} -
-
-
- - Steg {currentStep} av {totalSteps} - - - {stepTitles[currentStep - 1]} +
+ {/* ── Branded Header ── */} +
+ {/* Decorative elements */} +
+
+ + {String(currentStep).padStart(2, '0')} + +
+ +
+ {/* Top row: Logo + step indicator + counter */} +
+
+ Gnubok + gnubok +
+ {/* Step indicator — inline with logo row */} +
+ {STEP_INFO.map((_, i) => { + const num = i + 1 + return ( +
currentStep && 'w-4 bg-white/[0.1]', + )} + /> + ) + })} +
+ + {currentStep} / {totalSteps}
- -
-
- {/* Content */} -
- {renderSteps()} -
+ {/* Step title — compact */} +
+

+ {stepInfo.title} +

+

+ {stepInfo.subtitle} +

+
+
+
+ + {/* ── Form Content ── */} +
+
+
+ {renderSteps()} +
+
+
) } diff --git a/app/api/ai-consent/__tests__/route.test.ts b/app/api/ai-consent/__tests__/route.test.ts index 620c9ca3..549a02d3 100644 --- a/app/api/ai-consent/__tests__/route.test.ts +++ b/app/api/ai-consent/__tests__/route.test.ts @@ -31,6 +31,7 @@ function mockAuth(userId: string | null) { data: { user: userId ? { id: userId } : null }, }), }, + // eslint-disable-next-line @typescript-eslint/no-explicit-any } as any) } diff --git a/app/api/audit-trail/__tests__/route.test.ts b/app/api/audit-trail/__tests__/route.test.ts index 2310a4c0..976e72d2 100644 --- a/app/api/audit-trail/__tests__/route.test.ts +++ b/app/api/audit-trail/__tests__/route.test.ts @@ -23,6 +23,7 @@ function mockAuth(userId: string | null) { data: { user: userId ? { id: userId } : null }, }), }, + // eslint-disable-next-line @typescript-eslint/no-explicit-any } as any) } @@ -45,9 +46,11 @@ describe('GET /api/audit-trail', () => { { id: '1', action: 'INSERT', table_name: 'journal_entries', created_at: '2024-01-01T00:00:00Z' }, { id: '2', action: 'COMMIT', table_name: 'journal_entries', created_at: '2024-01-02T00:00:00Z' }, ] + // eslint-disable-next-line @typescript-eslint/no-explicit-any mockGetAuditLog.mockResolvedValue({ data: entries as any, count: 2 }) const req = createMockRequest('/api/audit-trail') + // eslint-disable-next-line @typescript-eslint/no-explicit-any const { status, body } = await parseJsonResponse<{ data: any[]; count: number }>(await GET(req)) expect(status).toBe(200) diff --git a/app/api/deadlines/[id]/route.ts b/app/api/deadlines/[id]/route.ts index 9e0ed7fe..9d9c21ec 100644 --- a/app/api/deadlines/[id]/route.ts +++ b/app/api/deadlines/[id]/route.ts @@ -60,7 +60,7 @@ export async function PUT( const body: Partial = await request.json() // First, get existing deadline to verify ownership - const { data: existing, error: fetchError } = await supabase + const { data: _existing, error: fetchError } = await supabase .from('deadlines') .select('*') .eq('id', id) diff --git a/app/api/import/bank-file/execute/route.ts b/app/api/import/bank-file/execute/route.ts index 8131c1bc..61f3d1ab 100644 --- a/app/api/import/bank-file/execute/route.ts +++ b/app/api/import/bank-file/execute/route.ts @@ -34,7 +34,7 @@ export async function POST(request: Request) { } const body: ExecuteRequest = await request.json() - const { transactions, format, filename, file_hash, skip_duplicates = true, auto_categorize = true } = body + const { transactions, format, filename, file_hash, skip_duplicates: _skip_duplicates = true, auto_categorize: _auto_categorize = true } = body if (!transactions || transactions.length === 0) { return NextResponse.json({ error: 'No transactions to import' }, { status: 400 }) diff --git a/app/api/invoices/[id]/mark-paid/__tests__/route.test.ts b/app/api/invoices/[id]/mark-paid/__tests__/route.test.ts index 502c52da..5c2a618c 100644 --- a/app/api/invoices/[id]/mark-paid/__tests__/route.test.ts +++ b/app/api/invoices/[id]/mark-paid/__tests__/route.test.ts @@ -89,7 +89,7 @@ describe('POST /api/invoices/[id]/mark-paid', () => { const request = createMockRequest('/api/invoices/inv-1/mark-paid', { method: 'POST' }) const response = await POST(request, createMockRouteParams({ id: 'inv-1' })) - const { status, body } = await parseJsonResponse<{ error: string }>(response) + const { status, body: _body } = await parseJsonResponse<{ error: string }>(response) expect(status).toBe(400) }) diff --git a/app/api/invoices/route.ts b/app/api/invoices/route.ts index 23ee6aef..90fd3023 100644 --- a/app/api/invoices/route.ts +++ b/app/api/invoices/route.ts @@ -4,7 +4,7 @@ import { eventBus } from '@/lib/events' import { ensureInitialized } from '@/lib/init' import { CreateInvoiceSchema, CreateCreditNoteSchema } from '@/lib/api/schemas' import type { EntityType, AccountingMethod, Invoice, CreditNote, InvoiceDocumentType } from '@/types' -import { getVatRules, calculateVat, calculateTotal, getAvailableVatRates, getVatTreatmentForRate } from '@/lib/invoices/vat-rules' +import { getVatRules, getAvailableVatRates } from '@/lib/invoices/vat-rules' import { fetchExchangeRate, convertToSEK } from '@/lib/currency/riksbanken' import { createCreditNoteJournalEntry, diff --git a/app/api/reports/audit-trail/__tests__/route.test.ts b/app/api/reports/audit-trail/__tests__/route.test.ts index fd661cf2..99a19e70 100644 --- a/app/api/reports/audit-trail/__tests__/route.test.ts +++ b/app/api/reports/audit-trail/__tests__/route.test.ts @@ -23,6 +23,7 @@ function mockAuth(userId: string | null) { data: { user: userId ? { id: userId } : null }, }), }, + // eslint-disable-next-line @typescript-eslint/no-explicit-any } as any) } @@ -67,6 +68,7 @@ describe('GET /api/reports/audit-trail', () => { it('returns CSV format with correct headers', async () => { mockAuth('user-1') + // eslint-disable-next-line @typescript-eslint/no-explicit-any mockGetAuditLog.mockResolvedValue({ data: sampleEntries as any, count: 2 }) const req = createMockRequest('/api/reports/audit-trail', { @@ -88,6 +90,7 @@ describe('GET /api/reports/audit-trail', () => { it('returns JSON format as downloadable file', async () => { mockAuth('user-1') + // eslint-disable-next-line @typescript-eslint/no-explicit-any mockGetAuditLog.mockResolvedValue({ data: sampleEntries as any, count: 2 }) const req = createMockRequest('/api/reports/audit-trail', { @@ -118,7 +121,9 @@ describe('GET /api/reports/audit-trail', () => { })) mockGetAuditLog + // eslint-disable-next-line @typescript-eslint/no-explicit-any .mockResolvedValueOnce({ data: bigPage as any, count: 600 }) + // eslint-disable-next-line @typescript-eslint/no-explicit-any .mockResolvedValueOnce({ data: lastPage as any, count: 600 }) const req = createMockRequest('/api/reports/audit-trail', { diff --git a/app/api/transactions/[id]/match-supplier-invoice/route.ts b/app/api/transactions/[id]/match-supplier-invoice/route.ts index 1adf8e55..232fb749 100644 --- a/app/api/transactions/[id]/match-supplier-invoice/route.ts +++ b/app/api/transactions/[id]/match-supplier-invoice/route.ts @@ -1,6 +1,5 @@ import { createClient } from '@/lib/supabase/server' import { NextResponse } from 'next/server' -import { findFiscalPeriod } from '@/lib/bookkeeping/engine' import { createSupplierInvoicePaymentEntry, createSupplierInvoiceCashEntry, @@ -9,41 +8,6 @@ import { validateBody } from '@/lib/api/validate' import { MatchSupplierInvoiceSchema } from '@/lib/api/schemas' import type { SupplierInvoice, SupplierInvoiceItem } from '@/types' -/** - * Ensure a fiscal period exists for the given date - */ -async function ensureFiscalPeriod( - supabase: Awaited>, - userId: string, - date: string -): Promise { - const existingPeriodId = await findFiscalPeriod(supabase, userId, date) - if (existingPeriodId) return existingPeriodId - - const transactionDate = new Date(date) - const year = transactionDate.getFullYear() - - const { data, error } = await supabase - .from('fiscal_periods') - .upsert({ - user_id: userId, - name: `Räkenskapsår ${year}`, - period_start: `${year}-01-01`, - period_end: `${year}-12-31`, - }, { - onConflict: 'user_id,period_start,period_end', - }) - .select('id') - .single() - - if (error) { - console.error('Failed to create fiscal period:', error) - return null - } - - return data?.id || null -} - /** * POST /api/transactions/[id]/match-supplier-invoice * diff --git a/app/api/transactions/suggest-categories/route.ts b/app/api/transactions/suggest-categories/route.ts index ec270dcd..d932ca7c 100644 --- a/app/api/transactions/suggest-categories/route.ts +++ b/app/api/transactions/suggest-categories/route.ts @@ -1,7 +1,7 @@ import { createClient } from '@/lib/supabase/server' import { NextResponse } from 'next/server' import { getSuggestedCategories, mergeAiSuggestions, getSuggestedTemplates, type SuggestedCategory, type SuggestedTemplate } from '@/lib/transactions/category-suggestions' -import type { Transaction, TransactionCategory, EntityType } from '@/types' +import type { Transaction, EntityType } from '@/types' /** * POST /api/transactions/suggest-categories diff --git a/app/page.tsx b/app/page.tsx index 7dfd3369..fe9eb92c 100644 --- a/app/page.tsx +++ b/app/page.tsx @@ -183,7 +183,7 @@ export default async function RootPage() { recentReceiptActivity.map((r) => new Date(r.created_at).toISOString().split('T')[0]) ) - let checkDate = new Date(todayDate) + const checkDate = new Date(todayDate) while (activityDates.has(checkDate.toISOString().split('T')[0])) { streakCount++ checkDate.setDate(checkDate.getDate() - 1) diff --git a/components/bookkeeping/ChartOfAccounts.tsx b/components/bookkeeping/ChartOfAccounts.tsx index 05418c4b..a4913d0c 100644 --- a/components/bookkeeping/ChartOfAccounts.tsx +++ b/components/bookkeeping/ChartOfAccounts.tsx @@ -29,10 +29,6 @@ export default function ChartOfAccounts() { const [editingSRU, setEditingSRU] = useState(null) const [sruValue, setSruValue] = useState('') - useEffect(() => { - fetchAccounts() - }, []) - async function fetchAccounts() { const res = await fetch('/api/bookkeeping/accounts') const { data } = await res.json() @@ -40,6 +36,10 @@ export default function ChartOfAccounts() { setLoading(false) } + useEffect(() => { + fetchAccounts() + }, []) + async function updateSRUCode(accountId: string, newSruCode: string) { const supabase = createClient() const trimmed = newSruCode.trim() || null diff --git a/components/bookkeeping/JournalEntryForm.tsx b/components/bookkeeping/JournalEntryForm.tsx index 8442396b..d90f0a52 100644 --- a/components/bookkeeping/JournalEntryForm.tsx +++ b/components/bookkeeping/JournalEntryForm.tsx @@ -70,11 +70,6 @@ export default function JournalEntryForm({ uploadedFiles.length > 0 useUnsavedChanges(hasContent) - useEffect(() => { - fetchPeriods() - fetchAccounts() - }, []) - async function fetchPeriods() { const res = await fetch('/api/bookkeeping/fiscal-periods') const { data } = await res.json() @@ -90,6 +85,11 @@ export default function JournalEntryForm({ setAccounts(data || []) } + useEffect(() => { + fetchPeriods() + fetchAccounts() + }, []) + const addLine = () => { setLines([...lines, { ...BLANK_LINE }]) } diff --git a/components/bookkeeping/JournalEntryList.tsx b/components/bookkeeping/JournalEntryList.tsx index d41ed9d6..d5158e0b 100644 --- a/components/bookkeeping/JournalEntryList.tsx +++ b/components/bookkeeping/JournalEntryList.tsx @@ -49,10 +49,6 @@ export default function JournalEntryList({ periodId }: Props) { } }, []) - useEffect(() => { - fetchEntries() - }, [periodId, page]) - async function fetchEntries() { setLoading(true) const params = new URLSearchParams({ @@ -73,6 +69,10 @@ export default function JournalEntryList({ periodId }: Props) { fetchAttachmentCounts(ids) } + useEffect(() => { + fetchEntries() + }, [periodId, page]) + const handleAttachmentCountChange = useCallback((entryId: string, count: number) => { setAttachmentCounts((prev) => ({ ...prev, [entryId]: count })) }, []) diff --git a/components/chat/ChatPanel.tsx b/components/chat/ChatPanel.tsx index 0df404f3..f196d41c 100644 --- a/components/chat/ChatPanel.tsx +++ b/components/chat/ChatPanel.tsx @@ -5,7 +5,7 @@ import { ChatMessage } from './ChatMessage' import { ChatInput } from './ChatInput' import { useChatStream } from './useChatStream' import { useToast } from '@/components/ui/use-toast' -import { Bot, Trash2, RotateCcw } from 'lucide-react' +import { Bot, RotateCcw } from 'lucide-react' import { Button } from '@/components/ui/button' interface ChatPanelProps { @@ -20,7 +20,7 @@ export function ChatPanel({ className }: ChatPanelProps) { messages, isLoading, isStreaming, - error, + error: _error, toolsExecuting, sendMessage, clearChat, diff --git a/components/chat/artifacts/ChatAgingBuckets.tsx b/components/chat/artifacts/ChatAgingBuckets.tsx index 2a0b64ae..430cdf22 100644 --- a/components/chat/artifacts/ChatAgingBuckets.tsx +++ b/components/chat/artifacts/ChatAgingBuckets.tsx @@ -18,8 +18,6 @@ interface ChatAgingBucketsProps { export function ChatAgingBuckets({ artifact }: ChatAgingBucketsProps) { const { title, buckets, total } = artifact - const maxAmount = Math.max(...buckets.map((b) => b.amount), 1) - return (
diff --git a/components/chat/artifacts/ChatDataTable.tsx b/components/chat/artifacts/ChatDataTable.tsx index aef58be4..caa329a9 100644 --- a/components/chat/artifacts/ChatDataTable.tsx +++ b/components/chat/artifacts/ChatDataTable.tsx @@ -6,7 +6,7 @@ interface ChatDataTableProps { artifact: TableArtifact } -function formatCell(value: string | number, align?: 'left' | 'right'): string { +function formatCell(value: string | number, _align?: 'left' | 'right'): string { if (typeof value === 'number') { return new Intl.NumberFormat('sv-SE', { minimumFractionDigits: value % 1 !== 0 ? 2 : 0, diff --git a/components/extensions/ExtensionCard.tsx b/components/extensions/ExtensionCard.tsx index 9316a0c4..df33a109 100644 --- a/components/extensions/ExtensionCard.tsx +++ b/components/extensions/ExtensionCard.tsx @@ -8,6 +8,7 @@ import ExtensionToggleButton from './ExtensionToggleButton' import Link from 'next/link' export default function ExtensionCard({ extension }: { extension: ExtensionDefinition }) { + const Icon = resolveIcon(extension.icon) return ( diff --git a/components/extensions/ExtensionWorkspaceLoader.tsx b/components/extensions/ExtensionWorkspaceLoader.tsx index 745125b6..fa88598e 100644 --- a/components/extensions/ExtensionWorkspaceLoader.tsx +++ b/components/extensions/ExtensionWorkspaceLoader.tsx @@ -16,6 +16,7 @@ export default function ExtensionWorkspaceLoader({ definition: ExtensionDefinition userId: string }) { + const WorkspaceComponent = getWorkspaceComponent(sector, slug) return ( diff --git a/components/extensions/ExtensionWorkspaceShell.tsx b/components/extensions/ExtensionWorkspaceShell.tsx index 9c0e4016..c813de90 100644 --- a/components/extensions/ExtensionWorkspaceShell.tsx +++ b/components/extensions/ExtensionWorkspaceShell.tsx @@ -12,6 +12,7 @@ export default function ExtensionWorkspaceShell({ definition: ExtensionDefinition children: React.ReactNode }) { + const Icon = resolveIcon(definition.icon) const sector = getSector(definition.sector) diff --git a/components/extensions/SectorCard.tsx b/components/extensions/SectorCard.tsx index 77a4d4c8..90b3ce9a 100644 --- a/components/extensions/SectorCard.tsx +++ b/components/extensions/SectorCard.tsx @@ -4,6 +4,7 @@ import { resolveIcon } from '@/lib/extensions/icon-resolver' import type { Sector } from '@/lib/extensions/types' export default function SectorCard({ sector }: { sector: Sector }) { + const Icon = resolveIcon(sector.icon) return ( diff --git a/components/import/BankFileResultStep.tsx b/components/import/BankFileResultStep.tsx index 8d6cd341..28e50818 100644 --- a/components/import/BankFileResultStep.tsx +++ b/components/import/BankFileResultStep.tsx @@ -8,7 +8,6 @@ import { XCircle, ArrowRight, RotateCcw, - ExternalLink, } from 'lucide-react' import type { IngestResult } from '@/lib/transactions/ingest' diff --git a/components/import/BankFileUploadStep.tsx b/components/import/BankFileUploadStep.tsx index 125c596d..067e6551 100644 --- a/components/import/BankFileUploadStep.tsx +++ b/components/import/BankFileUploadStep.tsx @@ -2,7 +2,6 @@ import { useState, useCallback } from 'react' import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card' -import { Button } from '@/components/ui/button' import { Badge } from '@/components/ui/badge' import { Progress } from '@/components/ui/progress' import { diff --git a/components/onboarding/Step1EntityType.tsx b/components/onboarding/Step1EntityType.tsx index 3baab2df..6fec0de1 100644 --- a/components/onboarding/Step1EntityType.tsx +++ b/components/onboarding/Step1EntityType.tsx @@ -45,15 +45,8 @@ export default function Step1EntityType({ initialData, onNext, isSaving }: Step1 } return ( -
-
-

Välkommen!

-

- Låt oss börja med att ställa in din verksamhet. Vilken företagsform har du? -

-
- -
+
+
{entityOptions.map((option) => { const Icon = option.icon const isSelected = selected === option.value diff --git a/components/onboarding/Step2CompanyDetails.tsx b/components/onboarding/Step2CompanyDetails.tsx index aff34e83..7e652099 100644 --- a/components/onboarding/Step2CompanyDetails.tsx +++ b/components/onboarding/Step2CompanyDetails.tsx @@ -57,13 +57,6 @@ export default function Step2CompanyDetails({ return (
-
-

Företagsuppgifter

-

- Fyll i uppgifter om din verksamhet. Dessa används på fakturor. -

-
- Grunduppgifter diff --git a/components/onboarding/Step3TaxRegistration.tsx b/components/onboarding/Step3TaxRegistration.tsx index aa13b2ab..aad133f7 100644 --- a/components/onboarding/Step3TaxRegistration.tsx +++ b/components/onboarding/Step3TaxRegistration.tsx @@ -263,13 +263,6 @@ export default function Step3TaxRegistration({ return (
-
-

Skatteregistrering

-

- Ange information om din skatteregistrering. -

-
- F-skatt och räkenskapsår diff --git a/components/onboarding/Step4PreliminaryTax.tsx b/components/onboarding/Step4PreliminaryTax.tsx index 7870423f..503d1b90 100644 --- a/components/onboarding/Step4PreliminaryTax.tsx +++ b/components/onboarding/Step4PreliminaryTax.tsx @@ -50,13 +50,6 @@ export default function Step4PreliminaryTax({ return (
-
-

Preliminärskatt

-

- Ange din månatliga F-skatt för att få bättre överblick över din ekonomi. -

-
- diff --git a/components/onboarding/Step6ConnectBank.tsx b/components/onboarding/Step6ConnectBank.tsx index c2841741..40744bc3 100644 --- a/components/onboarding/Step6ConnectBank.tsx +++ b/components/onboarding/Step6ConnectBank.tsx @@ -57,14 +57,6 @@ export default function Step6ConnectBank({ return (
-
-

Bankuppgifter

-

- Ange dina bankuppgifter så visas de på dina fakturor. - Du kan importera transaktioner från din bank efteråt. -

-
- diff --git a/components/ui/input.tsx b/components/ui/input.tsx index 54cba92b..0e00c0ff 100644 --- a/components/ui/input.tsx +++ b/components/ui/input.tsx @@ -1,8 +1,7 @@ import * as React from "react" import { cn } from "@/lib/utils" -export interface InputProps - extends React.InputHTMLAttributes {} +export type InputProps = React.InputHTMLAttributes const Input = React.forwardRef( ({ className, type, ...props }, ref) => { diff --git a/components/ui/textarea.tsx b/components/ui/textarea.tsx index 91a345a4..bb49eed0 100644 --- a/components/ui/textarea.tsx +++ b/components/ui/textarea.tsx @@ -1,8 +1,7 @@ import * as React from "react" import { cn } from "@/lib/utils" -export interface TextareaProps - extends React.TextareaHTMLAttributes {} +export type TextareaProps = React.TextareaHTMLAttributes const Textarea = React.forwardRef( ({ className, ...props }, ref) => { diff --git a/dev_docs/04-arkivplan-mall.md b/dev_docs/04-arkivplan-mall.md new file mode 100644 index 00000000..99581389 --- /dev/null +++ b/dev_docs/04-arkivplan-mall.md @@ -0,0 +1,146 @@ +# Arkivplan + +**Mall for användare av erp-base** +Upprättad i enlighet med BFNAR 2013:2 punkt 8.3 + +--- + +## Instruktioner + +Denna mall ska fyllas i av dig som kund och sparas som del av din systemdokumentation. Bokföringsnämndens allmänna råd (BFNAR 2013:2) kräver att varje bokföringsskyldig upprättar en arkivplan som beskriver vilken räkenskapsinformation som finns, var den förvaras, och vem som ansvarar för arkiveringen. + +Fyll i de markerade fälten. Radera denna instruktionssektion innan du arkiverar dokumentet. + +--- + +## 1. Företagsuppgifter + +| Fält | Uppgift | +|---|---| +| Företagsnamn | [FÖRETAGSNAMN] | +| Organisationsnummer | [ORG-NR] | +| Företagsform | [ ] Enskild firma [ ] Aktiebolag | +| Räkenskapsår | [STARTMÅNAD] - [SLUTMÅNAD] | +| Bokföringsmetod | [ ] Faktureringsmetoden [ ] Kontantmetoden | +| Momsredovisningsperiod | [ ] Månadsvis [ ] Kvartalsvis [ ] Årsvis | +| Ansvarig för bokföringen | [NAMN, ROLL] | + +## 2. Bokföringssystem + +| Fält | Uppgift | +|---|---| +| Programvara | erp-base ([DOMÄN]) | +| Leverantör | [BOLAGSNAMN], org.nr [ORG-NR] | +| Lagringsplats | Molnbaserad tjänst, data lagrat inom EU (Supabase/AWS) | +| Åtkomst | Via webbläsare, inloggning med magic link (e-post) | +| Kontoplan | BAS 2025/2026 (konfigurerad i erp-base) | + +## 3. Förteckning över räkenskapsinformation + +Tabellen nedan anger vilken räkenskapsinformation som finns, i vilken form den förvaras, var, och arkiveringstid. + +### 3.1 Löpande bokföring + +| Räkenskapsinformation | Form | Lagringsplats | Arkiveringstid | +|---|---|---|---| +| Grundbokföring (registreringsordning) | Elektronisk | erp-base databas | 7 år efter räkenskapsårets utgång | +| Huvudbokföring (systematisk ordning) | Elektronisk | erp-base databas | 7 år efter räkenskapsårets utgång | +| Verifikationer (journalposter) | Elektronisk | erp-base databas | 7 år efter räkenskapsårets utgång | + +### 3.2 Verifikationsunderlag + +| Räkenskapsinformation | Form | Lagringsplats | Arkiveringstid | Anmärkning | +|---|---|---|---|---| +| Kundfakturor (utgående) | Elektronisk (PDF) | erp-base dokumentarkiv | 7 år | Genereras i erp-base | +| Leverantörsfakturor (inkommande) | Elektronisk (PDF/bild) | erp-base dokumentarkiv | 7 år | Uppladdade/skannade | +| Kvitton | Elektronisk (foto/PDF) | erp-base dokumentarkiv | 7 år | Fotograferade via appen | +| Bankutdrag/kontoutdrag | Elektronisk | erp-base via PSD2-koppling | 7 år | Synkroniserade via Enable Banking | +| Avtal och övriga underlag | [Elektronisk/Papper] | [erp-base / Fysisk pärm] | 7 år | [Ange var dessa förvaras] | + +### 3.3 Årsbokslut och årsredovisning + +| Räkenskapsinformation | Form | Lagringsplats | Arkiveringstid | +|---|---|---|---| +| Resultaträkning | Elektronisk | erp-base rapportmodul | 7 år | +| Balansräkning | Elektronisk | erp-base rapportmodul | 7 år | +| Årsredovisning (AB) / Årsbokslut (EF) | [Elektronisk/Papper] | [erp-base / Bolagsverket / Fysisk pärm] | 7 år (10 år rekommenderat) | +| NE-bilaga (EF) | Elektronisk | erp-base rapportmodul | 7 år | +| SIE-filer (export) | Elektronisk | [Ange var exporterade filer sparas] | 7 år | + +### 3.4 Skattedeklarationer och momsrapporter + +| Räkenskapsinformation | Form | Lagringsplats | Arkiveringstid | +|---|---|---|---| +| Momsdeklarationer | Elektronisk | erp-base rapportmodul + Skatteverket | 7 år | +| SRU-filer | Elektronisk | erp-base rapportmodul | 7 år | +| Inkomstdeklaration | [Elektronisk/Papper] | [Skatteverket / Egen kopia] | 7 år | + +### 3.5 Systemdokumentation + +| Dokument | Form | Lagringsplats | Arkiveringstid | +|---|---|---|---| +| Systemdokumentation | Elektronisk | [erp-base / Egen lagring] | Samma som den räkenskapsinformation den avser | +| Behandlingshistorik | Elektronisk | erp-base (automatiskt genererad) | Samma som den räkenskapsinformation den avser | +| Denna arkivplan | [Elektronisk/Papper] | [Ange lagringsplats] | Samma som den räkenskapsinformation den avser | + +## 4. Pappersoriginal + +4.1. Räkenskapsinformation som tagits emot i pappersform (kvitton, fakturor) och som har överförts till elektronisk form genom skanning eller fotografering ska bevaras i sin ursprungliga pappersform i minst tre (3) år efter utgången av det kalenderår då räkenskapsåret avslutades, i enlighet med 7 kap. 6 § BFL. + +*Notering: Lagändring trädde i kraft 1 juli 2024 som möjliggör omedelbar förstöring av pappersoriginal efter överföring till elektronisk form, under förutsättning att överföringen sker på ett betryggande sätt och att inga uppgifter går förlorade. Se BFNAR 2024:1 och uppdaterad vägledning (2024-09-16) for detaljer om vilka krav som gäller vid sådan överföring.* + +4.2. Dokument som tas emot elektroniskt (e-fakturor, digitala kvitton) arkiveras i elektronisk form. Inget pappersoriginal finns. + +4.3. Förvaring av pappersoriginal: +- Plats: [ANGE PLATS, t.ex. kontor, bankfack] +- Ansvarig: [NAMN] + +## 5. Säkerhetskopiering och redundans + +5.1. erp-base sköter automatisk daglig säkerhetskopiering av databasen via Supabase-infrastrukturen. + +5.2. Kunden rekommenderas att regelbundet exportera SIE4-filer och spara dessa på en separat lagringsplats som kompletterande säkerhetskopia. + +Kundens kompletterande säkerhetskopiering: +- Frekvens: [t.ex. månadsvis, kvartalsvis] +- Lagringsplats: [t.ex. extern hårddisk, molnlagring] +- Ansvarig: [NAMN] + +## 6. Åtkomst efter avslutad prenumeration + +6.1. Vid uppsägning av erp-base-kontot har Kunden nittio (90) dagar att exportera all räkenskapsinformation i enlighet med Användarvillkoren avsnitt 8. + +6.2. Räkenskapsinformation som omfattas av sjuårig arkiveringsskyldighet bevaras i skrivskyddat läge av erp-base, alternativt tillhandahålls som fullständig dataexport. + +6.3. Det är Kundens ansvar att planera för dataportabilitet och säkerställa tillgång till räkenskapsinformation under hela arkiveringsperioden, oavsett om Tjänsten fortfarande används. + +## 7. Geografisk lagring + +7.1. All data i erp-base lagras inom EU/EES via Supabase (AWS-infrastruktur, region eu-central eller eu-west). + +7.2. Viss behandling sker hos underbiträden i USA (se Personuppgiftsbiträdesavtalet, avsnitt 6.2) med stöd av EU-U.S. Data Privacy Framework eller standardavtalsklausuler. + +7.3. I enlighet med 7 kap. 3a § BFL får räkenskapsinformation i elektronisk form förvaras i annat EU-land under förutsättning att detta har anmälts till Skatteverket. + +**Anmälan till Skatteverket:** [ ] Har gjorts [ ] Behöver göras [ ] Ej tillämpligt (data lagras i Sverige) + +## 8. Ansvar och kontakt + +| Roll | Namn | Kontakt | +|---|---|---| +| Bokföringsansvarig | [NAMN] | [E-POST / TELEFON] | +| Extern redovisningskonsult (om tillämpligt) | [NAMN / BYRÅ] | [E-POST / TELEFON] | +| Revisor (om tillämpligt) | [NAMN / BYRÅ] | [E-POST / TELEFON] | + +## 9. Uppdatering av arkivplanen + +Denna arkivplan ska granskas och vid behov uppdateras minst en gång per räkenskapsår, samt vid byte av bokföringsprogram, ändring av företagsform, eller ändring av lagringsrutiner. + +| Datum | Ändring | Utförd av | +|---|---|---| +| [DATUM] | Första version upprättad | [NAMN] | +| | | | + +--- + +*Denna arkivplan uppfyller kraven i BFNAR 2013:2 punkt 8.3 och Exempel 8.1 i vägledningen. Anpassa innehållet till ditt företags specifika förhållanden. Platshållare markerade med hakparenteser ska fyllas i.* diff --git a/dev_docs/05-systemdokumentation-mall.md b/dev_docs/05-systemdokumentation-mall.md new file mode 100644 index 00000000..cbe0f1df --- /dev/null +++ b/dev_docs/05-systemdokumentation-mall.md @@ -0,0 +1,259 @@ +# Systemdokumentation + +**Mall for användare av erp-base** +Upprättad i enlighet med 5 kap. 11 § BFL och BFNAR 2013:2 kapitel 9 + +--- + +## Instruktioner + +Varje bokföringsskyldig ska upprätta en systemdokumentation som beskriver bokföringssystemets organisation och uppbyggnad. Dokumentationen ska göra det möjligt att utan svårighet överblicka systemet och förstå hur bokföringen är organiserad (BFNAR 2013:2 punkt 9.1). + +Denna mall är förifylld med uppgifter som gäller för erp-base. Avsnitt markerade med hakparenteser ska anpassas till ditt företags förhållanden. Radera denna instruktionssektion innan du arkiverar dokumentet. + +Systemdokumentationen ska förvaras tillsammans med övrig räkenskapsinformation under hela arkiveringsperioden (7 år). + +--- + +## 1. Företagsuppgifter + +| Fält | Uppgift | +|---|---| +| Företagsnamn | [FÖRETAGSNAMN] | +| Organisationsnummer | [ORG-NR] | +| Företagsform | [ ] Enskild firma [ ] Aktiebolag | +| Räkenskapsår | [STARTMÅNAD] - [SLUTMÅNAD] | +| Tillämpat K-regelverk | [ ] K1 (förenklat årsbokslut, EF under 3 MSEK) [ ] K2 (årsredovisning, mindre AB) [ ] K3 (årsredovisning, huvudregelverk) | + +## 2. Bokföringsprogram + +| Fält | Uppgift | +|---|---| +| Programnamn | erp-base | +| Version | [ANGE VERSION, t.ex. 1.0] | +| Leverantör | [BOLAGSNAMN], org.nr [ORG-NR] | +| Webbplats | [DOMÄN] | +| Typ | Molnbaserad SaaS-tjänst (webbläsarbaserad) | +| Databasplattform | PostgreSQL via Supabase (AWS, EU-region) | +| Autentisering | Magic link via e-post (lösenordsfri) | + +## 3. Kontoplan (BFNAR 2013:2 punkt 9.2) + +3.1. Kontoplanen bygger på BAS-kontoplanen (BAS 2025/2026) utgiven av BAS-intressenternas Förening. + +3.2. Kontona är indelade i klasser enligt BAS-standard: + +| Klass | Beskrivning | Exempel på konton | +|---|---|---| +| 1 | Tillgångar | 1510 Kundfordringar, 1930 Företagskonto | +| 2 | Eget kapital och skulder | 2013 Egna uttag (EF), 2440 Leverantörsskulder, 2611-2631 Utgående moms, 2641 Ingående moms | +| 3 | Intäkter | 3001 Försäljning 25%, 3002 Försäljning 12%, 3003 Försäljning 6%, 3305 Exportförsäljning | +| 4-7 | Kostnader | Konfigureras efter verksamhet | +| 8 | Finansiella poster och skatt | Konfigureras efter verksamhet | + +3.3. Kontoplanen kan ses och exporteras i erp-base under Inställningar > Kontoplan. + +3.4. Företagsspecifika anpassningar av kontoplanen: +[BESKRIV EVENTUELLA TILLAGDA ELLER BORTTAGNA KONTON, t.ex. "Konto 4010 Inköp varor, 5010 Lokalhyra har lagts till. Inga standardkonton har tagits bort."] + +## 4. Samlingsplan (BFNAR 2013:2 punkt 9.3-9.5) + +Samlingsplanen beskriver hur bokföringen är organiserad i form av delsystem, grundbokföring och huvudbokföring. + +### 4.1 Översikt + +``` +Affärshändelse + | + v +Verifikation skapas (manuellt eller automatiskt) + | + v +Journalpost registreras (grundbokföring, registreringsordning) + | + v +Konteras på BAS-konton (huvudbokföring, systematisk ordning) + | + v +Status: Utkast (draft) + | + v +Bekräftas av användaren + | + v +Status: Bokförd (posted), verifikationsnummer tilldelas +``` + +### 4.2 Grundbokföring (registreringsordning) + +Samtliga affärshändelser registreras kronologiskt i erp-base journalen. Varje post innehåller: +- Verifikationsnummer (sekventiellt, tilldelat automatiskt vid bokföring) +- Registreringsdatum (datum då posten skapades i systemet) +- Bokföringsdatum (datum för affärshändelsen) +- Beskrivning +- Konteringsrader med konto, debet, kredit + +Grundbokföringen kan visas under Bokföring > Journal i erp-base. + +### 4.3 Huvudbokföring (systematisk ordning) + +Huvudbokföringen presenterar affärshändelserna sorterade per konto. Varje konto visar ingående saldo, periodens transaktioner och utgående saldo. + +Huvudbokföringen kan visas och exporteras under Rapporter > Huvudbok i erp-base. + +### 4.4 Delsystem + +Följande delsystem matar journalen: + +| Delsystem | Beskrivning | Automatisk kontering | +|---|---|---| +| Kundfakturering | Utgående fakturor med per-rad momssats | Debet 1510, kredit 30xx + 26xx | +| Kundbetalningar | Inbetalningar mot fakturor | Debet 1930, kredit 1510 | +| Leverantörsfakturor | Inkommande fakturor, registrering och betalning | Debet kostnadskonto + 2641, kredit 2440 | +| Leverantörsbetalningar | Utbetalningar mot leverantörsfakturor | Debet 2440, kredit 1930 | +| Banktransaktioner | Synkroniserade via PSD2 (Enable Banking) | Kontering via kategoriseringsregler | +| Kvittohantering | OCR-bearbetade kvitton | Kontering efter granskning | +| Kreditnotor | Kreditering av utgående fakturor | Omvänd kontering av originalfaktura | + +### 4.5 Avstämningsordning + +Bankkonto 1930 avstäms via erp-base bankavstämningsmodul (4-stegs matchning: exakt belopp+datum, referensmatchning, datumintervall, fuzzy-matchning). + +## 5. Verifikationer (BFNAR 2013:2 punkt 9.6-9.8) + +### 5.1 Verifikationsnumrering + +Verifikationsnummer tilldelas sekventiellt av systemet vid bokföring. Numreringen är unik per räkenskapsår och användare. Numren tilldelas via databas-RPC (concurrent-safe) och kan inte sättas manuellt. + +En enda verifikationsnummerserie används: [A1, A2, A3, ...]. + +[OM FÖRETAGET ANVÄNDER FLERA SERIER, BESKRIV HÄR.] + +### 5.2 Verifikationens innehåll + +Varje verifikation i erp-base innehåller: +- Verifikationsnummer +- Bokföringsdatum (affärshändelsens datum) +- Registreringsdatum (datum då posten skapades) +- Beskrivning av affärshändelsen +- Konteringsrader (konto, debet, kredit) +- Referens till underlag (bifogat dokument, fakturanummer, etc.) +- Status (utkast / bokförd / reverserad) +- Vid rättelse: referens till reverserad/reverserande verifikation + +### 5.3 Underlag + +Underlag kopplas till verifikationer som bifogade dokument (PDF, bild). Dokumenten lagras i erp-base dokumentarkiv med SHA-256 checksumma for integritetskontroll. + +Typer av underlag: +- Kundfakturor (genererade i systemet) +- Leverantörsfakturor (uppladdade) +- Kvitton (fotograferade/skannade) +- Bankbekräftelser (synkroniserade) +- Övriga avtal och dokument (uppladdade) + +## 6. Rättelser (BFNAR 2013:2 punkt 9.9) + +6.1. Bokförda verifikationer (status: posted) kan inte ändras eller raderas. Detta upprätthålls av databastriggrar i enlighet med bokföringslagens krav på oföränderlighet. + +6.2. Rättelse sker genom stornobokning: en ny verifikation skapas som reverserar den felaktiga posten (byter debet/kredit). Den nya verifikationen länkas till originalet via referens (reverses_id / reversed_by_id). + +6.3. Därefter skapas en ny korrekt verifikation vid behov. + +6.4. Rättelseverifikationen innehåller uppgift om vilken verifikation som rättats, när rättelsen gjordes, och vem som utförde rättelsen (BFNAR 2013:2 punkt 2.17). + +## 7. Periodavstängning + +7.1. Räkenskapsperioder kan stängas (låsas) i erp-base. En låst period tillåter inte nya bokföringsposter. Periodlåsning upprätthålls av databastriggrar (enforce_period_lock). + +7.2. Årsbokslut registreras som bokföringsposter i systemet. + +## 8. Momshantering + +8.1. Följande momssatser hanteras: + +| Momssats | Beskrivning | Utgående moms-konto | Ingående moms-konto | +|---|---|---|---| +| 25 % | Standardsats | 2611 | 2641 | +| 12 % | Reducerad (livsmedel, hotell m.m.) | 2621 | 2641 | +| 6 % | Reducerad (böcker, tidningar, kultur m.m.) | 2631 | 2641 | +| 0 % (export) | Varuexport utanför EU | - | 2641 | +| 0 % (omvänd skattskyldighet) | Försäljning med omvänd skattskyldighet | - | 2641/2645 | +| Momsfri | Undantagna transaktioner | - | - | + +*Notering: Livsmedel sänks till 6 % från 1 april 2026 t.o.m. 31 december 2027.* + +8.2. Fakturor stödjer blandade momssatser (per fakturarad). + +8.3. Momsrapport genereras under Rapporter > Momsdeklaration och mappas till Skatteverkets rutor. + +## 9. Behandlingshistorik (BFNAR 2013:2 punkt 9.16) + +9.1. erp-base registrerar automatiskt en behandlingshistorik som inkluderar: +- Registreringsdatum och tidpunkt for varje journalpost +- Tidpunkt för statusändring (utkast till bokförd) +- Vem som utförde bokningen (användar-ID kopplat till e-postadress) +- Stornobokningar med referens till originalverifikation +- Tidpunkt och utförare av periodlåsning + +9.2. Behandlingshistoriken genereras automatiskt av systemet och kan inte ändras av användaren. + +9.3. Behandlingshistoriken kan exporteras under Rapporter > Audit trail. + +## 10. Import och export + +| Funktion | Format | Beskrivning | +|---|---|---| +| SIE-import | SIE4 | Import av bokföringsdata från annat system | +| Bankfil-import | CSV (10 svenska bankformat) | Import av banktransaktioner | +| SIE-export | SIE4 | Export av komplett bokföring per räkenskapsår | +| Huvudbok | PDF/skärm | Export av huvudbok | +| Resultaträkning | PDF/skärm | Export av resultaträkning | +| Balansräkning | PDF/skärm | Export av balansräkning | +| Momsdeklaration | PDF/skärm | Underlag för momsdeklaration | +| SRU-export | SRU | Export for inkomstdeklaration | +| NE-bilaga | PDF/skärm | Bilaga till inkomstdeklaration (EF) | +| Verifikationsunderlag | PDF/bild | Nedladdning av bifogade dokument | + +## 11. Integrationer + +| Integration | Beskrivning | Dataflöde | +|---|---|---| +| Enable Banking (PSD2) | Bankkontosynkronisering | Bank -> erp-base (läsning av transaktioner och saldon) | +| Anthropic API | AI-kategorisering av transaktioner, OCR | erp-base -> Anthropic -> erp-base (transaktionsdata skickas, kategoriseringsförslag returneras) | +| OpenAI API | Embeddingar for likhetsmatchning | erp-base -> OpenAI -> erp-base (transaktionsbeskrivningar skickas, vektorer returneras) | +| Resend | E-postutskick | erp-base -> Resend -> mottagare (fakturor, påminnelser) | + +[ANGE YTTERLIGARE INTEGRATIONER OM TILLÄMPLIGT] + +## 12. Behörigheter och åtkomstkontroll + +12.1. Varje konto i erp-base är isolerat via Row Level Security (RLS) i databasen. En användare kan enbart se och redigera sin egen data. + +12.2. Nuvarande behörighetsstruktur: + +| Roll | Beskrivning | +|---|---| +| Kontoägare | Full åtkomst till all data och funktionalitet | + +[OM YTTERLIGARE ROLLER FINNS, BESKRIV HÄR] + +12.3. Ansvarig for att tilldela och granska behörigheter: [NAMN] + +## 13. Uppdatering av systemdokumentationen + +Systemdokumentationen ska uppdateras vid: +- Byte eller uppgradering av bokföringsprogram +- Ändringar i kontoplan +- Ändringar i momshantering +- Nya integrationer eller delsystem +- Minst en gång per räkenskapsår + +| Datum | Ändring | Utförd av | +|---|---|---| +| [DATUM] | Första version upprättad | [NAMN] | +| | | | + +--- + +*Denna systemdokumentation uppfyller kraven i 5 kap. 11 § BFL och BFNAR 2013:2 kapitel 9 (punkterna 9.1-9.16) samt Exempel 9.1-9.4 i vägledningen. Anpassa innehållet till ditt företags specifika förhållanden.* diff --git a/eslint.config.mjs b/eslint.config.mjs index 05e726d1..df5b4e42 100644 --- a/eslint.config.mjs +++ b/eslint.config.mjs @@ -5,6 +5,15 @@ import nextTs from "eslint-config-next/typescript"; const eslintConfig = defineConfig([ ...nextVitals, ...nextTs, + { + rules: { + "@typescript-eslint/no-unused-vars": ["warn", { + argsIgnorePattern: "^_", + varsIgnorePattern: "^_", + destructuredArrayIgnorePattern: "^_", + }], + }, + }, // Override default ignores of eslint-config-next. globalIgnores([ // Default ignores of eslint-config-next: diff --git a/extensions.md b/extensions.md index 0c9421b0..784a0a27 100644 --- a/extensions.md +++ b/extensions.md @@ -2,7 +2,7 @@ ## The App -erp-base is a Swedish accounting platform for sole traders (enskild firma) and limited companies (aktiebolag). It handles the legally required bookkeeping and financial management that every Swedish business needs. +gnubok is a Swedish accounting platform for sole traders (enskild firma) and limited companies (aktiebolag). It handles the legally required bookkeeping and financial management that every Swedish business needs. ## Core Functionality @@ -678,7 +678,7 @@ Core runs with zero extensions. This gives you the standard accounting system: b ```bash # 1. Clone and install -git clone && cd erp-base +git clone && cd gnubok npm install # 2. Set environment variables (minimum 4) diff --git a/extensions/general/ai-categorization/index.ts b/extensions/general/ai-categorization/index.ts index aa209789..df2f25d1 100644 --- a/extensions/general/ai-categorization/index.ts +++ b/extensions/general/ai-categorization/index.ts @@ -10,7 +10,6 @@ import { type CategorizationSuggestion, type AccountUsageEntry, type MerchantHistoryEntry, - type TrackingContext, } from './categorizer' import type { BookingTemplate } from '@/lib/bookkeeping/booking-templates' @@ -198,9 +197,9 @@ async function handleTransactionSynced( // Helpers // ============================================================ -// eslint-disable-next-line @typescript-eslint/no-explicit-any async function buildEnrichedContext( userId: string, + // eslint-disable-next-line @typescript-eslint/no-explicit-any supabase: any, transactions: TransactionForCategorization[] ): Promise { diff --git a/extensions/general/ai-chat/api-routes.ts b/extensions/general/ai-chat/api-routes.ts index 297691f3..c8a2af23 100644 --- a/extensions/general/ai-chat/api-routes.ts +++ b/extensions/general/ai-chat/api-routes.ts @@ -1,6 +1,6 @@ import { NextResponse } from 'next/server' import type { ApiRouteDefinition, ExtensionContext } from '@/lib/extensions/types' -import { generateChatResponse, streamChatResponse, streamRoutedResponse } from '@/extensions/general/ai-chat/chatbot/chain' +import { generateChatResponse, streamRoutedResponse } from '@/extensions/general/ai-chat/chatbot/chain' import { CHATBOT_CONFIG } from '@/extensions/general/ai-chat/chatbot/config' import type { ChatMessage, ChatRequest, SourceReference, ArtifactSpec } from '@/types/chat' diff --git a/extensions/general/ai-chat/chatbot/chain.ts b/extensions/general/ai-chat/chatbot/chain.ts index 31f5ec7e..6772d672 100644 --- a/extensions/general/ai-chat/chatbot/chain.ts +++ b/extensions/general/ai-chat/chatbot/chain.ts @@ -9,7 +9,6 @@ import { import { retrieveRelevantDocuments, documentsToSources, - type RetrievedDocument, } from './retriever' import { routeMessage, type RouteType } from './router' import { createAccountingTools } from './tools' @@ -171,7 +170,7 @@ export async function* streamRoutedResponse( conversationHistory: ChatMessage[], supabase: SupabaseClient, userId: string, - sessionId?: string + _sessionId?: string ): AsyncGenerator { // 1. Route the message const { route, rewrittenQuery } = await routeMessage(userMessage, conversationHistory) diff --git a/extensions/general/ai-chat/chatbot/tools.ts b/extensions/general/ai-chat/chatbot/tools.ts index cbeedc48..1f39ac42 100644 --- a/extensions/general/ai-chat/chatbot/tools.ts +++ b/extensions/general/ai-chat/chatbot/tools.ts @@ -65,7 +65,7 @@ export function createAccountingTools(supabase: SupabaseClient, userId: string) if (date_from) query = query.gte('invoice_date', date_from) if (date_to) query = query.lte('invoice_date', date_to) - const { data, error, count } = await supabase + const { data: _data, error: _countError, count } = await supabase .from('invoices') .select('id', { count: 'exact', head: true }) .eq('user_id', userId) diff --git a/extensions/general/ai-chat/ingestion/ingest.ts b/extensions/general/ai-chat/ingestion/ingest.ts index e1b01920..3df6e6eb 100644 --- a/extensions/general/ai-chat/ingestion/ingest.ts +++ b/extensions/general/ai-chat/ingestion/ingest.ts @@ -144,7 +144,7 @@ function chunkText(text: string, maxSize: number, overlap: number): string[] { */ function extractMetadata( content: string, - sectionTitle: string + _sectionTitle: string ): Record { const metadata: Record = {} diff --git a/extensions/general/calendar/components/CalendarDayCell.tsx b/extensions/general/calendar/components/CalendarDayCell.tsx index b1553887..37d5f581 100644 --- a/extensions/general/calendar/components/CalendarDayCell.tsx +++ b/extensions/general/calendar/components/CalendarDayCell.tsx @@ -1,7 +1,7 @@ 'use client' import { cn } from '@/lib/utils' -import { Invoice, Deadline, DeadlineStatus } from '@/types' +import { Invoice, Deadline } from '@/types' import { isInvoiceOverdue, isSameDay, formatDateISO, STATUS_COLORS } from '@/lib/calendar/utils' interface CalendarDayCellProps { diff --git a/extensions/general/calendar/components/CalendarWeekView.tsx b/extensions/general/calendar/components/CalendarWeekView.tsx index eb02d9b7..0523726e 100644 --- a/extensions/general/calendar/components/CalendarWeekView.tsx +++ b/extensions/general/calendar/components/CalendarWeekView.tsx @@ -131,7 +131,7 @@ export function CalendarWeekView({ {/* Time grid */}
- {timeSlots.map((time, timeIndex) => ( + {timeSlots.map((time, _timeIndex) => (
{time} diff --git a/extensions/general/calendar/components/DayDetailModal.tsx b/extensions/general/calendar/components/DayDetailModal.tsx index 1340583b..e0d4066e 100644 --- a/extensions/general/calendar/components/DayDetailModal.tsx +++ b/extensions/general/calendar/components/DayDetailModal.tsx @@ -10,7 +10,7 @@ import { import { Badge } from '@/components/ui/badge' import { Button } from '@/components/ui/button' import { Invoice, Deadline } from '@/types' -import { formatCurrency, formatDate } from '@/lib/utils' +import { formatCurrency } from '@/lib/utils' import { isInvoiceOverdue, isDeadlineOverdue, diff --git a/extensions/general/receipt-ocr/components/ReceiptDashboard.tsx b/extensions/general/receipt-ocr/components/ReceiptDashboard.tsx index 7f9046b3..bcc28202 100644 --- a/extensions/general/receipt-ocr/components/ReceiptDashboard.tsx +++ b/extensions/general/receipt-ocr/components/ReceiptDashboard.tsx @@ -1,6 +1,6 @@ 'use client' -import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card' +import { Card, CardContent } from '@/components/ui/card' import { Button } from '@/components/ui/button' import { Badge } from '@/components/ui/badge' import { Flame, Receipt, CreditCard, Camera, ArrowRight } from 'lucide-react' diff --git a/extensions/general/receipt-ocr/components/TransactionMatcher.tsx b/extensions/general/receipt-ocr/components/TransactionMatcher.tsx index a929b606..fb75c56a 100644 --- a/extensions/general/receipt-ocr/components/TransactionMatcher.tsx +++ b/extensions/general/receipt-ocr/components/TransactionMatcher.tsx @@ -1,13 +1,13 @@ 'use client' import { useState, useEffect } from 'react' -import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card' +import { Card, CardContent } from '@/components/ui/card' import { Button } from '@/components/ui/button' import { Badge } from '@/components/ui/badge' import { Skeleton } from '@/components/ui/skeleton' import { formatCurrency, formatDate } from '@/lib/utils' import { X, Check, Link2, AlertCircle, Search, Loader2 } from 'lucide-react' -import type { Receipt, ReceiptMatchCandidate, Transaction } from '@/types' +import type { Receipt, ReceiptMatchCandidate } from '@/types' interface TransactionMatcherProps { receipt: Receipt diff --git a/extensions/general/receipt-ocr/lib/receipt-categorizer.ts b/extensions/general/receipt-ocr/lib/receipt-categorizer.ts index 0649bc29..62b0afe7 100644 --- a/extensions/general/receipt-ocr/lib/receipt-categorizer.ts +++ b/extensions/general/receipt-ocr/lib/receipt-categorizer.ts @@ -5,7 +5,7 @@ * using pattern matching and AI-suggested categories. */ -import type { TransactionCategory, ReceiptLineItem, ExtractedLineItem } from '@/types' +import type { TransactionCategory, ExtractedLineItem } from '@/types' import { getTemplateById } from '@/lib/bookkeeping/booking-templates' // Category mappings from suggested category to TransactionCategory diff --git a/extensions/general/receipt-ocr/lib/receipt-pipeline.ts b/extensions/general/receipt-ocr/lib/receipt-pipeline.ts index e63510b1..2e80592f 100644 --- a/extensions/general/receipt-ocr/lib/receipt-pipeline.ts +++ b/extensions/general/receipt-ocr/lib/receipt-pipeline.ts @@ -9,7 +9,7 @@ import 'server-only' import type { SupabaseClient } from '@supabase/supabase-js' -import type { Receipt, ReceiptMatchCandidate, ReceiptExtractionResult } from '@/types' +import type { Receipt, ReceiptExtractionResult } from '@/types' import { analyzeReceipt } from './receipt-analyzer' import { processLineItems, getDefaultClassification } from './receipt-categorizer' import { autoMatchReceipts } from './receipt-matcher' diff --git a/extensions/general/receipt-ocr/pages/ReceiptsPage.tsx b/extensions/general/receipt-ocr/pages/ReceiptsPage.tsx index e7023ba2..8902de25 100644 --- a/extensions/general/receipt-ocr/pages/ReceiptsPage.tsx +++ b/extensions/general/receipt-ocr/pages/ReceiptsPage.tsx @@ -1,7 +1,7 @@ 'use client' import { useState, useEffect, useCallback } from 'react' -import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card' +import { Card, CardContent } from '@/components/ui/card' import { Button } from '@/components/ui/button' import { Badge } from '@/components/ui/badge' import { Skeleton } from '@/components/ui/skeleton' diff --git a/lib/ai/document-analyzer.ts b/lib/ai/document-analyzer.ts index 17db1433..2c817247 100644 --- a/lib/ai/document-analyzer.ts +++ b/lib/ai/document-analyzer.ts @@ -451,7 +451,7 @@ function validateVatBreakdown(data: any): VatBreakdownItem[] { */ export function validateExtractionConsistency( extraction: ReceiptExtractionResult | InvoiceExtractionResult, - // eslint-disable-next-line @typescript-eslint/no-unused-vars + _type: 'receipt' | 'invoice' ): ConsistencyResult { const issues: string[] = [] diff --git a/lib/api/schemas.ts b/lib/api/schemas.ts index 28597c71..162366bd 100644 --- a/lib/api/schemas.ts +++ b/lib/api/schemas.ts @@ -13,9 +13,6 @@ const isoDate = z.string().regex(/^\d{4}-\d{2}-\d{2}$/, 'Expected YYYY-MM-DD dat /** BAS account number — always a string of 4 digits */ const accountNumber = z.string().regex(/^\d{4}$/, 'Account number must be exactly 4 digits') -/** Positive monetary amount (> 0) */ -const positiveAmount = z.number().positive() - /** Non-negative monetary amount (>= 0) */ const nonNegativeAmount = z.number().nonnegative() diff --git a/lib/bookkeeping/account-descriptions.ts b/lib/bookkeeping/account-descriptions.ts index 9fffb944..f0e8101d 100644 --- a/lib/bookkeeping/account-descriptions.ts +++ b/lib/bookkeeping/account-descriptions.ts @@ -1,3 +1,5 @@ +import { getBASReference, ACCOUNT_CLASS_LABELS } from './bas-reference' + export type AccountType = 'asset' | 'liability' | 'equity' | 'revenue' | 'expense' | 'untaxed_reserves' export interface AccountDescription { @@ -332,8 +334,6 @@ export function getAccountDescription(accountNumber: string): AccountDescription // Fall back to BAS reference data for accounts not in the hardcoded list try { - // Dynamic import avoided — use lazy require pattern - const { getBASReference, ACCOUNT_CLASS_LABELS } = require('./bas-reference') const ref = getBASReference(accountNumber) if (ref) { const classLabel = ACCOUNT_CLASS_LABELS[ref.account_class] || '' diff --git a/lib/bookkeeping/currency-revaluation.ts b/lib/bookkeeping/currency-revaluation.ts index 2c1ef123..5e04be34 100644 --- a/lib/bookkeeping/currency-revaluation.ts +++ b/lib/bookkeeping/currency-revaluation.ts @@ -173,10 +173,8 @@ export async function previewCurrencyRevaluation( let credit1510 = 0 // Receivable loss (revalue down) let debit2440 = 0 // Payable gain (liability shrank) let credit2440 = 0 // Payable loss (liability grew) - let debit3960 = 0 // Placeholder — we won't debit 3960 let credit3960 = 0 // Gains let debit7960 = 0 // Losses - let credit7960 = 0 // Placeholder — we won't credit 7960 for (const item of items) { if (item.type === 'receivable') { diff --git a/lib/bookkeeping/invoice-entries.ts b/lib/bookkeeping/invoice-entries.ts index b1a7d94e..666d8ae2 100644 --- a/lib/bookkeeping/invoice-entries.ts +++ b/lib/bookkeeping/invoice-entries.ts @@ -1,6 +1,6 @@ import { createJournalEntry, findFiscalPeriod } from './engine' import { resolveSekAmount, buildCurrencyMetadata } from './currency-utils' -import { generateSalesVatLines, generateReverseChargeLines } from './vat-entries' +import { generateSalesVatLines } from './vat-entries' import { getVatTreatmentForRate } from '@/lib/invoices/vat-rules' import { createLogger } from '@/lib/logger' import type { SupabaseClient } from '@supabase/supabase-js' diff --git a/lib/bookkeeping/mapping-engine.ts b/lib/bookkeeping/mapping-engine.ts index b2db7de7..584ac5f0 100644 --- a/lib/bookkeeping/mapping-engine.ts +++ b/lib/bookkeeping/mapping-engine.ts @@ -2,14 +2,11 @@ import type { SupabaseClient } from '@supabase/supabase-js' import { generateInputVatLine, generateReverseChargeLines, - extractNetAmount, - extractVatAmount, } from './vat-entries' import { findMatchingTemplates, buildMappingResultFromTemplate } from './booking-templates' import type { MappingRule, MappingResult, - RiskLevel, Transaction, EntityType, VatJournalLine, diff --git a/lib/bookkeeping/transaction-entries.ts b/lib/bookkeeping/transaction-entries.ts index 4be60f64..8edc4818 100644 --- a/lib/bookkeeping/transaction-entries.ts +++ b/lib/bookkeeping/transaction-entries.ts @@ -1,6 +1,6 @@ import { createJournalEntry, findFiscalPeriod } from './engine' import { resolveSekAmount, buildCurrencyMetadata } from './currency-utils' -import { generateInputVatLine, generateReverseChargeLines, extractNetAmount, extractVatAmount } from './vat-entries' +import { extractNetAmount, extractVatAmount } from './vat-entries' import { createLogger } from '@/lib/logger' import type { SupabaseClient } from '@supabase/supabase-js' import type { diff --git a/lib/core/documents/document-service.ts b/lib/core/documents/document-service.ts index 2a4fe893..362a4165 100644 --- a/lib/core/documents/document-service.ts +++ b/lib/core/documents/document-service.ts @@ -1,7 +1,7 @@ import type { SupabaseClient } from '@supabase/supabase-js' import { createServiceClient } from '@/lib/supabase/server' import { eventBus } from '@/lib/events' -import type { DocumentAttachment, CreateDocumentAttachmentInput, DocumentUploadSource } from '@/types' +import type { DocumentAttachment, DocumentUploadSource } from '@/types' /** * Document Service - WORM-style document archive diff --git a/lib/documents/__tests__/document-matcher.test.ts b/lib/documents/__tests__/document-matcher.test.ts index da8b5dda..50863faa 100644 --- a/lib/documents/__tests__/document-matcher.test.ts +++ b/lib/documents/__tests__/document-matcher.test.ts @@ -1,7 +1,7 @@ import { describe, it, expect, vi, beforeEach } from 'vitest' import { matchDocumentToTransactions } from '../document-matcher' import { makeInvoiceInboxItem, makeTransaction } from '@/tests/helpers' -import type { InvoiceExtractionResult, ReceiptExtractionResult, Transaction } from '@/types' +import type { InvoiceExtractionResult, ReceiptExtractionResult } from '@/types' describe('matchDocumentToTransactions', () => { const mockSupabase = {} as never // Not used when candidateTransactions is provided diff --git a/lib/extensions/__tests__/ai-consent.test.ts b/lib/extensions/__tests__/ai-consent.test.ts index 81bc6a88..46f16855 100644 --- a/lib/extensions/__tests__/ai-consent.test.ts +++ b/lib/extensions/__tests__/ai-consent.test.ts @@ -1,3 +1,4 @@ +/* eslint-disable @typescript-eslint/no-explicit-any */ import { describe, it, expect, vi, beforeEach } from 'vitest' import { createMockSupabase } from '@/tests/helpers' import { diff --git a/lib/extensions/__tests__/sectors.test.ts b/lib/extensions/__tests__/sectors.test.ts index 2da84e51..90224143 100644 --- a/lib/extensions/__tests__/sectors.test.ts +++ b/lib/extensions/__tests__/sectors.test.ts @@ -1,4 +1,4 @@ -import { resolve, join, dirname } from 'path' +import { resolve, join } from 'path' import { readdirSync, readFileSync } from 'fs' /** @@ -74,6 +74,7 @@ describe('sectors registry', () => { }) it('getSector returns undefined for unknown slug', () => { + // eslint-disable-next-line @typescript-eslint/no-explicit-any const sector = getSector('invalid' as any) expect(sector).toBeUndefined() }) diff --git a/lib/import/sie-import.ts b/lib/import/sie-import.ts index a2f2b6fa..f5d773f6 100644 --- a/lib/import/sie-import.ts +++ b/lib/import/sie-import.ts @@ -355,7 +355,7 @@ async function importVouchers( p_series: voucherSeries, }) - let currentVoucherNumber = (startNumber as number) || 1 + const currentVoucherNumber = (startNumber as number) || 1 // Batch insert journal entries (in chunks of 100) const BATCH_SIZE = 100 diff --git a/lib/reports/__tests__/ar-ledger.test.ts b/lib/reports/__tests__/ar-ledger.test.ts index 2f1ac1d2..bb4174ac 100644 --- a/lib/reports/__tests__/ar-ledger.test.ts +++ b/lib/reports/__tests__/ar-ledger.test.ts @@ -20,6 +20,7 @@ function makeBuilder() { function makeClient() { return { from: vi.fn().mockImplementation(() => makeBuilder()), + // eslint-disable-next-line @typescript-eslint/no-explicit-any } as any } diff --git a/lib/reports/__tests__/ar-reconciliation.test.ts b/lib/reports/__tests__/ar-reconciliation.test.ts index 03b7923c..d6a3f951 100644 --- a/lib/reports/__tests__/ar-reconciliation.test.ts +++ b/lib/reports/__tests__/ar-reconciliation.test.ts @@ -20,6 +20,7 @@ function makeBuilder() { function makeClient() { return { from: vi.fn().mockImplementation(() => makeBuilder()), + // eslint-disable-next-line @typescript-eslint/no-explicit-any } as any } diff --git a/lib/reports/__tests__/balance-sheet.test.ts b/lib/reports/__tests__/balance-sheet.test.ts index fa00e2fc..7efefa0a 100644 --- a/lib/reports/__tests__/balance-sheet.test.ts +++ b/lib/reports/__tests__/balance-sheet.test.ts @@ -10,6 +10,7 @@ import type { TrialBalanceRow } from '@/types' const mockTrialBalance = vi.mocked(generateTrialBalance) +// eslint-disable-next-line @typescript-eslint/no-explicit-any const supabase = {} as any beforeEach(() => { diff --git a/lib/reports/__tests__/full-archive-export.test.ts b/lib/reports/__tests__/full-archive-export.test.ts index 70031e02..5e6d18e3 100644 --- a/lib/reports/__tests__/full-archive-export.test.ts +++ b/lib/reports/__tests__/full-archive-export.test.ts @@ -1,3 +1,4 @@ +/* eslint-disable @typescript-eslint/no-explicit-any */ import { describe, it, expect, vi, beforeEach } from 'vitest' import JSZip from 'jszip' import { generateFullArchive } from '../full-archive-export' diff --git a/lib/reports/__tests__/general-ledger.test.ts b/lib/reports/__tests__/general-ledger.test.ts index dcda4be7..628a1b2d 100644 --- a/lib/reports/__tests__/general-ledger.test.ts +++ b/lib/reports/__tests__/general-ledger.test.ts @@ -21,6 +21,7 @@ function makeClient() { return { from: vi.fn().mockImplementation(() => makeBuilder()), rpc: vi.fn().mockImplementation(async () => results[resultIdx++] ?? { data: null, error: null }), + // eslint-disable-next-line @typescript-eslint/no-explicit-any } as any } diff --git a/lib/reports/__tests__/income-statement.test.ts b/lib/reports/__tests__/income-statement.test.ts index 7b40e4e2..3f583135 100644 --- a/lib/reports/__tests__/income-statement.test.ts +++ b/lib/reports/__tests__/income-statement.test.ts @@ -10,6 +10,7 @@ import type { TrialBalanceRow } from '@/types' const mockTrialBalance = vi.mocked(generateTrialBalance) +// eslint-disable-next-line @typescript-eslint/no-explicit-any const supabase = {} as any beforeEach(() => { diff --git a/lib/reports/__tests__/journal-register.test.ts b/lib/reports/__tests__/journal-register.test.ts index c3b2329a..5a2cd567 100644 --- a/lib/reports/__tests__/journal-register.test.ts +++ b/lib/reports/__tests__/journal-register.test.ts @@ -20,6 +20,7 @@ function makeBuilder() { function makeClient() { return { from: vi.fn().mockImplementation(() => makeBuilder()), + // eslint-disable-next-line @typescript-eslint/no-explicit-any } as any } diff --git a/lib/reports/__tests__/sie-export.test.ts b/lib/reports/__tests__/sie-export.test.ts index e6d6e918..4359caac 100644 --- a/lib/reports/__tests__/sie-export.test.ts +++ b/lib/reports/__tests__/sie-export.test.ts @@ -20,6 +20,7 @@ function makeBuilder() { function makeClient() { return { from: vi.fn().mockImplementation(() => makeBuilder()), + // eslint-disable-next-line @typescript-eslint/no-explicit-any } as any } diff --git a/lib/reports/__tests__/supplier-ledger.test.ts b/lib/reports/__tests__/supplier-ledger.test.ts index dd53a23f..cefc51b0 100644 --- a/lib/reports/__tests__/supplier-ledger.test.ts +++ b/lib/reports/__tests__/supplier-ledger.test.ts @@ -20,6 +20,7 @@ function makeBuilder() { function makeClient() { return { from: vi.fn().mockImplementation(() => makeBuilder()), + // eslint-disable-next-line @typescript-eslint/no-explicit-any } as any } diff --git a/lib/reports/__tests__/supplier-reconciliation.test.ts b/lib/reports/__tests__/supplier-reconciliation.test.ts index c60b60a5..dc5ef35b 100644 --- a/lib/reports/__tests__/supplier-reconciliation.test.ts +++ b/lib/reports/__tests__/supplier-reconciliation.test.ts @@ -20,6 +20,7 @@ function makeBuilder() { function makeClient() { return { from: vi.fn().mockImplementation(() => makeBuilder()), + // eslint-disable-next-line @typescript-eslint/no-explicit-any } as any } diff --git a/lib/reports/__tests__/trial-balance.test.ts b/lib/reports/__tests__/trial-balance.test.ts index 14444fa8..7dd9beac 100644 --- a/lib/reports/__tests__/trial-balance.test.ts +++ b/lib/reports/__tests__/trial-balance.test.ts @@ -21,6 +21,7 @@ function makeClient() { return { from: vi.fn().mockImplementation(() => makeBuilder()), rpc: vi.fn().mockImplementation(async () => results[resultIdx++] ?? { data: null, error: null }), + // eslint-disable-next-line @typescript-eslint/no-explicit-any } as any } diff --git a/lib/reports/__tests__/vat-declaration.test.ts b/lib/reports/__tests__/vat-declaration.test.ts index 47325085..0c0a962f 100644 --- a/lib/reports/__tests__/vat-declaration.test.ts +++ b/lib/reports/__tests__/vat-declaration.test.ts @@ -20,6 +20,7 @@ function makeBuilder() { function makeClient() { return { from: vi.fn().mockImplementation(() => makeBuilder()), + // eslint-disable-next-line @typescript-eslint/no-explicit-any } as any } diff --git a/lib/reports/ink2/__tests__/ink2-engine.test.ts b/lib/reports/ink2/__tests__/ink2-engine.test.ts index d0878208..c31801fb 100644 --- a/lib/reports/ink2/__tests__/ink2-engine.test.ts +++ b/lib/reports/ink2/__tests__/ink2-engine.test.ts @@ -1,6 +1,6 @@ import { describe, it, expect } from 'vitest' import { INK2_ACCOUNT_MAPPINGS, isAccountInMapping } from '../ink2-engine' -import type { INK2AccountMapping, INK2SRUCode } from '../types' +import type { INK2SRUCode } from '../types' /** * Helper to find which SRU code an account maps to diff --git a/lib/reports/ne-bilaga/__tests__/ne-engine.test.ts b/lib/reports/ne-bilaga/__tests__/ne-engine.test.ts index c52a29c5..eee7a977 100644 --- a/lib/reports/ne-bilaga/__tests__/ne-engine.test.ts +++ b/lib/reports/ne-bilaga/__tests__/ne-engine.test.ts @@ -1,6 +1,5 @@ import { describe, it, expect } from 'vitest' import { NE_ACCOUNT_MAPPINGS } from '../ne-engine' -import type { NEAccountMapping } from '@/lib/reports/ne-bilaga/types' /** * Helper to check if an account falls into a specific ruta diff --git a/lib/tax/calculator.ts b/lib/tax/calculator.ts index 6f8afc42..a74f76e4 100644 --- a/lib/tax/calculator.ts +++ b/lib/tax/calculator.ts @@ -137,8 +137,6 @@ export function calculateABTax( // Arbetsgivaravgifter on salaries (already included in salary costs usually) // This is just for reference - const arbetsgivaravgifter = salaryCostsYTD * TAX_RATES.arbetsgivaravgifter - const totalTax = bolagsskatt + momsFromUnpaidInvoices return { diff --git a/lib/tax/deadline-config.ts b/lib/tax/deadline-config.ts index 16b7f54a..60bdb5f7 100644 --- a/lib/tax/deadline-config.ts +++ b/lib/tax/deadline-config.ts @@ -214,7 +214,6 @@ export const TAX_DEADLINE_CONFIGS: TaxDeadlineConfig[] = [ const dl = getDeadline(fyEndYear) if (dl.year === year) { // Compute the FY start year - const fyStartYear = fyEndMonth === 12 ? fyEndYear : fyEndYear - (fyEndMonth < settings.fiscal_year_start_month ? 0 : 0) const fyStart = fyEndMonth === 12 ? fyEndYear : fyEndYear const periodLabel = fyEndMonth === 12 ? `${fyEndYear}` @@ -249,14 +248,12 @@ export const TAX_DEADLINE_CONFIGS: TaxDeadlineConfig[] = [ // 7 months after FY end per ÅRL 8:3 // Deadline month (0-indexed): ((fyEndMonth - 1) + 7) % 12 - const deadlineMonth0 = ((fyEndMonth - 1) + 7) % 12 // Last day of the deadline month // Determine which year the deadline falls in - const wrapsYear = fyEndMonth > 5 // Jun+ wraps into next year + const _wrapsYear = fyEndMonth > 5 // Jun+ wraps into next year // For calendar year (Dec end): deadline Jul 31 same year+1 // The FY ending in `year` produces a deadline: - const fyEndYear = year - 1 // By default we show deadline for the FY that ended in year-1 - const deadlineYear = wrapsYear ? fyEndYear + 1 + 1 : fyEndYear + 1 + const _fyEndYear = year - 1 // By default we show deadline for the FY that ended in year-1 // Simpler: compute from a concrete FY end date // FY ends: fyEndMonth (1-indexed), last day, in some year. // We want the deadline that falls in `year`. diff --git a/lib/tax/deadline-generator.ts b/lib/tax/deadline-generator.ts index 32f2f9ab..dfbdb2cd 100644 --- a/lib/tax/deadline-generator.ts +++ b/lib/tax/deadline-generator.ts @@ -4,11 +4,10 @@ import { SupabaseClient } from '@supabase/supabase-js' import { createLogger } from '@/lib/logger' -import type { TaxDeadlineType, DeadlineStatus, CreateDeadlineInput } from '@/types' +import type { TaxDeadlineType, DeadlineStatus } from '@/types' const log = createLogger('deadline-generator') import { - TAX_DEADLINE_CONFIGS, getApplicableDeadlineConfigs, type CompanySettingsForDeadlines, type DeadlineInstance, @@ -180,7 +179,7 @@ export async function generateTaxDeadlinesForUser( */ function createLinkedReportPeriod( instance: DeadlineInstance, - type: TaxDeadlineType + _type: TaxDeadlineType ): Record | null { const period = instance.period diff --git a/public/docs/arkivplan-mall.md b/public/docs/arkivplan-mall.md new file mode 100644 index 00000000..99581389 --- /dev/null +++ b/public/docs/arkivplan-mall.md @@ -0,0 +1,146 @@ +# Arkivplan + +**Mall for användare av erp-base** +Upprättad i enlighet med BFNAR 2013:2 punkt 8.3 + +--- + +## Instruktioner + +Denna mall ska fyllas i av dig som kund och sparas som del av din systemdokumentation. Bokföringsnämndens allmänna råd (BFNAR 2013:2) kräver att varje bokföringsskyldig upprättar en arkivplan som beskriver vilken räkenskapsinformation som finns, var den förvaras, och vem som ansvarar för arkiveringen. + +Fyll i de markerade fälten. Radera denna instruktionssektion innan du arkiverar dokumentet. + +--- + +## 1. Företagsuppgifter + +| Fält | Uppgift | +|---|---| +| Företagsnamn | [FÖRETAGSNAMN] | +| Organisationsnummer | [ORG-NR] | +| Företagsform | [ ] Enskild firma [ ] Aktiebolag | +| Räkenskapsår | [STARTMÅNAD] - [SLUTMÅNAD] | +| Bokföringsmetod | [ ] Faktureringsmetoden [ ] Kontantmetoden | +| Momsredovisningsperiod | [ ] Månadsvis [ ] Kvartalsvis [ ] Årsvis | +| Ansvarig för bokföringen | [NAMN, ROLL] | + +## 2. Bokföringssystem + +| Fält | Uppgift | +|---|---| +| Programvara | erp-base ([DOMÄN]) | +| Leverantör | [BOLAGSNAMN], org.nr [ORG-NR] | +| Lagringsplats | Molnbaserad tjänst, data lagrat inom EU (Supabase/AWS) | +| Åtkomst | Via webbläsare, inloggning med magic link (e-post) | +| Kontoplan | BAS 2025/2026 (konfigurerad i erp-base) | + +## 3. Förteckning över räkenskapsinformation + +Tabellen nedan anger vilken räkenskapsinformation som finns, i vilken form den förvaras, var, och arkiveringstid. + +### 3.1 Löpande bokföring + +| Räkenskapsinformation | Form | Lagringsplats | Arkiveringstid | +|---|---|---|---| +| Grundbokföring (registreringsordning) | Elektronisk | erp-base databas | 7 år efter räkenskapsårets utgång | +| Huvudbokföring (systematisk ordning) | Elektronisk | erp-base databas | 7 år efter räkenskapsårets utgång | +| Verifikationer (journalposter) | Elektronisk | erp-base databas | 7 år efter räkenskapsårets utgång | + +### 3.2 Verifikationsunderlag + +| Räkenskapsinformation | Form | Lagringsplats | Arkiveringstid | Anmärkning | +|---|---|---|---|---| +| Kundfakturor (utgående) | Elektronisk (PDF) | erp-base dokumentarkiv | 7 år | Genereras i erp-base | +| Leverantörsfakturor (inkommande) | Elektronisk (PDF/bild) | erp-base dokumentarkiv | 7 år | Uppladdade/skannade | +| Kvitton | Elektronisk (foto/PDF) | erp-base dokumentarkiv | 7 år | Fotograferade via appen | +| Bankutdrag/kontoutdrag | Elektronisk | erp-base via PSD2-koppling | 7 år | Synkroniserade via Enable Banking | +| Avtal och övriga underlag | [Elektronisk/Papper] | [erp-base / Fysisk pärm] | 7 år | [Ange var dessa förvaras] | + +### 3.3 Årsbokslut och årsredovisning + +| Räkenskapsinformation | Form | Lagringsplats | Arkiveringstid | +|---|---|---|---| +| Resultaträkning | Elektronisk | erp-base rapportmodul | 7 år | +| Balansräkning | Elektronisk | erp-base rapportmodul | 7 år | +| Årsredovisning (AB) / Årsbokslut (EF) | [Elektronisk/Papper] | [erp-base / Bolagsverket / Fysisk pärm] | 7 år (10 år rekommenderat) | +| NE-bilaga (EF) | Elektronisk | erp-base rapportmodul | 7 år | +| SIE-filer (export) | Elektronisk | [Ange var exporterade filer sparas] | 7 år | + +### 3.4 Skattedeklarationer och momsrapporter + +| Räkenskapsinformation | Form | Lagringsplats | Arkiveringstid | +|---|---|---|---| +| Momsdeklarationer | Elektronisk | erp-base rapportmodul + Skatteverket | 7 år | +| SRU-filer | Elektronisk | erp-base rapportmodul | 7 år | +| Inkomstdeklaration | [Elektronisk/Papper] | [Skatteverket / Egen kopia] | 7 år | + +### 3.5 Systemdokumentation + +| Dokument | Form | Lagringsplats | Arkiveringstid | +|---|---|---|---| +| Systemdokumentation | Elektronisk | [erp-base / Egen lagring] | Samma som den räkenskapsinformation den avser | +| Behandlingshistorik | Elektronisk | erp-base (automatiskt genererad) | Samma som den räkenskapsinformation den avser | +| Denna arkivplan | [Elektronisk/Papper] | [Ange lagringsplats] | Samma som den räkenskapsinformation den avser | + +## 4. Pappersoriginal + +4.1. Räkenskapsinformation som tagits emot i pappersform (kvitton, fakturor) och som har överförts till elektronisk form genom skanning eller fotografering ska bevaras i sin ursprungliga pappersform i minst tre (3) år efter utgången av det kalenderår då räkenskapsåret avslutades, i enlighet med 7 kap. 6 § BFL. + +*Notering: Lagändring trädde i kraft 1 juli 2024 som möjliggör omedelbar förstöring av pappersoriginal efter överföring till elektronisk form, under förutsättning att överföringen sker på ett betryggande sätt och att inga uppgifter går förlorade. Se BFNAR 2024:1 och uppdaterad vägledning (2024-09-16) for detaljer om vilka krav som gäller vid sådan överföring.* + +4.2. Dokument som tas emot elektroniskt (e-fakturor, digitala kvitton) arkiveras i elektronisk form. Inget pappersoriginal finns. + +4.3. Förvaring av pappersoriginal: +- Plats: [ANGE PLATS, t.ex. kontor, bankfack] +- Ansvarig: [NAMN] + +## 5. Säkerhetskopiering och redundans + +5.1. erp-base sköter automatisk daglig säkerhetskopiering av databasen via Supabase-infrastrukturen. + +5.2. Kunden rekommenderas att regelbundet exportera SIE4-filer och spara dessa på en separat lagringsplats som kompletterande säkerhetskopia. + +Kundens kompletterande säkerhetskopiering: +- Frekvens: [t.ex. månadsvis, kvartalsvis] +- Lagringsplats: [t.ex. extern hårddisk, molnlagring] +- Ansvarig: [NAMN] + +## 6. Åtkomst efter avslutad prenumeration + +6.1. Vid uppsägning av erp-base-kontot har Kunden nittio (90) dagar att exportera all räkenskapsinformation i enlighet med Användarvillkoren avsnitt 8. + +6.2. Räkenskapsinformation som omfattas av sjuårig arkiveringsskyldighet bevaras i skrivskyddat läge av erp-base, alternativt tillhandahålls som fullständig dataexport. + +6.3. Det är Kundens ansvar att planera för dataportabilitet och säkerställa tillgång till räkenskapsinformation under hela arkiveringsperioden, oavsett om Tjänsten fortfarande används. + +## 7. Geografisk lagring + +7.1. All data i erp-base lagras inom EU/EES via Supabase (AWS-infrastruktur, region eu-central eller eu-west). + +7.2. Viss behandling sker hos underbiträden i USA (se Personuppgiftsbiträdesavtalet, avsnitt 6.2) med stöd av EU-U.S. Data Privacy Framework eller standardavtalsklausuler. + +7.3. I enlighet med 7 kap. 3a § BFL får räkenskapsinformation i elektronisk form förvaras i annat EU-land under förutsättning att detta har anmälts till Skatteverket. + +**Anmälan till Skatteverket:** [ ] Har gjorts [ ] Behöver göras [ ] Ej tillämpligt (data lagras i Sverige) + +## 8. Ansvar och kontakt + +| Roll | Namn | Kontakt | +|---|---|---| +| Bokföringsansvarig | [NAMN] | [E-POST / TELEFON] | +| Extern redovisningskonsult (om tillämpligt) | [NAMN / BYRÅ] | [E-POST / TELEFON] | +| Revisor (om tillämpligt) | [NAMN / BYRÅ] | [E-POST / TELEFON] | + +## 9. Uppdatering av arkivplanen + +Denna arkivplan ska granskas och vid behov uppdateras minst en gång per räkenskapsår, samt vid byte av bokföringsprogram, ändring av företagsform, eller ändring av lagringsrutiner. + +| Datum | Ändring | Utförd av | +|---|---|---| +| [DATUM] | Första version upprättad | [NAMN] | +| | | | + +--- + +*Denna arkivplan uppfyller kraven i BFNAR 2013:2 punkt 8.3 och Exempel 8.1 i vägledningen. Anpassa innehållet till ditt företags specifika förhållanden. Platshållare markerade med hakparenteser ska fyllas i.* diff --git a/public/docs/systemdokumentation-mall.md b/public/docs/systemdokumentation-mall.md new file mode 100644 index 00000000..cbe0f1df --- /dev/null +++ b/public/docs/systemdokumentation-mall.md @@ -0,0 +1,259 @@ +# Systemdokumentation + +**Mall for användare av erp-base** +Upprättad i enlighet med 5 kap. 11 § BFL och BFNAR 2013:2 kapitel 9 + +--- + +## Instruktioner + +Varje bokföringsskyldig ska upprätta en systemdokumentation som beskriver bokföringssystemets organisation och uppbyggnad. Dokumentationen ska göra det möjligt att utan svårighet överblicka systemet och förstå hur bokföringen är organiserad (BFNAR 2013:2 punkt 9.1). + +Denna mall är förifylld med uppgifter som gäller för erp-base. Avsnitt markerade med hakparenteser ska anpassas till ditt företags förhållanden. Radera denna instruktionssektion innan du arkiverar dokumentet. + +Systemdokumentationen ska förvaras tillsammans med övrig räkenskapsinformation under hela arkiveringsperioden (7 år). + +--- + +## 1. Företagsuppgifter + +| Fält | Uppgift | +|---|---| +| Företagsnamn | [FÖRETAGSNAMN] | +| Organisationsnummer | [ORG-NR] | +| Företagsform | [ ] Enskild firma [ ] Aktiebolag | +| Räkenskapsår | [STARTMÅNAD] - [SLUTMÅNAD] | +| Tillämpat K-regelverk | [ ] K1 (förenklat årsbokslut, EF under 3 MSEK) [ ] K2 (årsredovisning, mindre AB) [ ] K3 (årsredovisning, huvudregelverk) | + +## 2. Bokföringsprogram + +| Fält | Uppgift | +|---|---| +| Programnamn | erp-base | +| Version | [ANGE VERSION, t.ex. 1.0] | +| Leverantör | [BOLAGSNAMN], org.nr [ORG-NR] | +| Webbplats | [DOMÄN] | +| Typ | Molnbaserad SaaS-tjänst (webbläsarbaserad) | +| Databasplattform | PostgreSQL via Supabase (AWS, EU-region) | +| Autentisering | Magic link via e-post (lösenordsfri) | + +## 3. Kontoplan (BFNAR 2013:2 punkt 9.2) + +3.1. Kontoplanen bygger på BAS-kontoplanen (BAS 2025/2026) utgiven av BAS-intressenternas Förening. + +3.2. Kontona är indelade i klasser enligt BAS-standard: + +| Klass | Beskrivning | Exempel på konton | +|---|---|---| +| 1 | Tillgångar | 1510 Kundfordringar, 1930 Företagskonto | +| 2 | Eget kapital och skulder | 2013 Egna uttag (EF), 2440 Leverantörsskulder, 2611-2631 Utgående moms, 2641 Ingående moms | +| 3 | Intäkter | 3001 Försäljning 25%, 3002 Försäljning 12%, 3003 Försäljning 6%, 3305 Exportförsäljning | +| 4-7 | Kostnader | Konfigureras efter verksamhet | +| 8 | Finansiella poster och skatt | Konfigureras efter verksamhet | + +3.3. Kontoplanen kan ses och exporteras i erp-base under Inställningar > Kontoplan. + +3.4. Företagsspecifika anpassningar av kontoplanen: +[BESKRIV EVENTUELLA TILLAGDA ELLER BORTTAGNA KONTON, t.ex. "Konto 4010 Inköp varor, 5010 Lokalhyra har lagts till. Inga standardkonton har tagits bort."] + +## 4. Samlingsplan (BFNAR 2013:2 punkt 9.3-9.5) + +Samlingsplanen beskriver hur bokföringen är organiserad i form av delsystem, grundbokföring och huvudbokföring. + +### 4.1 Översikt + +``` +Affärshändelse + | + v +Verifikation skapas (manuellt eller automatiskt) + | + v +Journalpost registreras (grundbokföring, registreringsordning) + | + v +Konteras på BAS-konton (huvudbokföring, systematisk ordning) + | + v +Status: Utkast (draft) + | + v +Bekräftas av användaren + | + v +Status: Bokförd (posted), verifikationsnummer tilldelas +``` + +### 4.2 Grundbokföring (registreringsordning) + +Samtliga affärshändelser registreras kronologiskt i erp-base journalen. Varje post innehåller: +- Verifikationsnummer (sekventiellt, tilldelat automatiskt vid bokföring) +- Registreringsdatum (datum då posten skapades i systemet) +- Bokföringsdatum (datum för affärshändelsen) +- Beskrivning +- Konteringsrader med konto, debet, kredit + +Grundbokföringen kan visas under Bokföring > Journal i erp-base. + +### 4.3 Huvudbokföring (systematisk ordning) + +Huvudbokföringen presenterar affärshändelserna sorterade per konto. Varje konto visar ingående saldo, periodens transaktioner och utgående saldo. + +Huvudbokföringen kan visas och exporteras under Rapporter > Huvudbok i erp-base. + +### 4.4 Delsystem + +Följande delsystem matar journalen: + +| Delsystem | Beskrivning | Automatisk kontering | +|---|---|---| +| Kundfakturering | Utgående fakturor med per-rad momssats | Debet 1510, kredit 30xx + 26xx | +| Kundbetalningar | Inbetalningar mot fakturor | Debet 1930, kredit 1510 | +| Leverantörsfakturor | Inkommande fakturor, registrering och betalning | Debet kostnadskonto + 2641, kredit 2440 | +| Leverantörsbetalningar | Utbetalningar mot leverantörsfakturor | Debet 2440, kredit 1930 | +| Banktransaktioner | Synkroniserade via PSD2 (Enable Banking) | Kontering via kategoriseringsregler | +| Kvittohantering | OCR-bearbetade kvitton | Kontering efter granskning | +| Kreditnotor | Kreditering av utgående fakturor | Omvänd kontering av originalfaktura | + +### 4.5 Avstämningsordning + +Bankkonto 1930 avstäms via erp-base bankavstämningsmodul (4-stegs matchning: exakt belopp+datum, referensmatchning, datumintervall, fuzzy-matchning). + +## 5. Verifikationer (BFNAR 2013:2 punkt 9.6-9.8) + +### 5.1 Verifikationsnumrering + +Verifikationsnummer tilldelas sekventiellt av systemet vid bokföring. Numreringen är unik per räkenskapsår och användare. Numren tilldelas via databas-RPC (concurrent-safe) och kan inte sättas manuellt. + +En enda verifikationsnummerserie används: [A1, A2, A3, ...]. + +[OM FÖRETAGET ANVÄNDER FLERA SERIER, BESKRIV HÄR.] + +### 5.2 Verifikationens innehåll + +Varje verifikation i erp-base innehåller: +- Verifikationsnummer +- Bokföringsdatum (affärshändelsens datum) +- Registreringsdatum (datum då posten skapades) +- Beskrivning av affärshändelsen +- Konteringsrader (konto, debet, kredit) +- Referens till underlag (bifogat dokument, fakturanummer, etc.) +- Status (utkast / bokförd / reverserad) +- Vid rättelse: referens till reverserad/reverserande verifikation + +### 5.3 Underlag + +Underlag kopplas till verifikationer som bifogade dokument (PDF, bild). Dokumenten lagras i erp-base dokumentarkiv med SHA-256 checksumma for integritetskontroll. + +Typer av underlag: +- Kundfakturor (genererade i systemet) +- Leverantörsfakturor (uppladdade) +- Kvitton (fotograferade/skannade) +- Bankbekräftelser (synkroniserade) +- Övriga avtal och dokument (uppladdade) + +## 6. Rättelser (BFNAR 2013:2 punkt 9.9) + +6.1. Bokförda verifikationer (status: posted) kan inte ändras eller raderas. Detta upprätthålls av databastriggrar i enlighet med bokföringslagens krav på oföränderlighet. + +6.2. Rättelse sker genom stornobokning: en ny verifikation skapas som reverserar den felaktiga posten (byter debet/kredit). Den nya verifikationen länkas till originalet via referens (reverses_id / reversed_by_id). + +6.3. Därefter skapas en ny korrekt verifikation vid behov. + +6.4. Rättelseverifikationen innehåller uppgift om vilken verifikation som rättats, när rättelsen gjordes, och vem som utförde rättelsen (BFNAR 2013:2 punkt 2.17). + +## 7. Periodavstängning + +7.1. Räkenskapsperioder kan stängas (låsas) i erp-base. En låst period tillåter inte nya bokföringsposter. Periodlåsning upprätthålls av databastriggrar (enforce_period_lock). + +7.2. Årsbokslut registreras som bokföringsposter i systemet. + +## 8. Momshantering + +8.1. Följande momssatser hanteras: + +| Momssats | Beskrivning | Utgående moms-konto | Ingående moms-konto | +|---|---|---|---| +| 25 % | Standardsats | 2611 | 2641 | +| 12 % | Reducerad (livsmedel, hotell m.m.) | 2621 | 2641 | +| 6 % | Reducerad (böcker, tidningar, kultur m.m.) | 2631 | 2641 | +| 0 % (export) | Varuexport utanför EU | - | 2641 | +| 0 % (omvänd skattskyldighet) | Försäljning med omvänd skattskyldighet | - | 2641/2645 | +| Momsfri | Undantagna transaktioner | - | - | + +*Notering: Livsmedel sänks till 6 % från 1 april 2026 t.o.m. 31 december 2027.* + +8.2. Fakturor stödjer blandade momssatser (per fakturarad). + +8.3. Momsrapport genereras under Rapporter > Momsdeklaration och mappas till Skatteverkets rutor. + +## 9. Behandlingshistorik (BFNAR 2013:2 punkt 9.16) + +9.1. erp-base registrerar automatiskt en behandlingshistorik som inkluderar: +- Registreringsdatum och tidpunkt for varje journalpost +- Tidpunkt för statusändring (utkast till bokförd) +- Vem som utförde bokningen (användar-ID kopplat till e-postadress) +- Stornobokningar med referens till originalverifikation +- Tidpunkt och utförare av periodlåsning + +9.2. Behandlingshistoriken genereras automatiskt av systemet och kan inte ändras av användaren. + +9.3. Behandlingshistoriken kan exporteras under Rapporter > Audit trail. + +## 10. Import och export + +| Funktion | Format | Beskrivning | +|---|---|---| +| SIE-import | SIE4 | Import av bokföringsdata från annat system | +| Bankfil-import | CSV (10 svenska bankformat) | Import av banktransaktioner | +| SIE-export | SIE4 | Export av komplett bokföring per räkenskapsår | +| Huvudbok | PDF/skärm | Export av huvudbok | +| Resultaträkning | PDF/skärm | Export av resultaträkning | +| Balansräkning | PDF/skärm | Export av balansräkning | +| Momsdeklaration | PDF/skärm | Underlag för momsdeklaration | +| SRU-export | SRU | Export for inkomstdeklaration | +| NE-bilaga | PDF/skärm | Bilaga till inkomstdeklaration (EF) | +| Verifikationsunderlag | PDF/bild | Nedladdning av bifogade dokument | + +## 11. Integrationer + +| Integration | Beskrivning | Dataflöde | +|---|---|---| +| Enable Banking (PSD2) | Bankkontosynkronisering | Bank -> erp-base (läsning av transaktioner och saldon) | +| Anthropic API | AI-kategorisering av transaktioner, OCR | erp-base -> Anthropic -> erp-base (transaktionsdata skickas, kategoriseringsförslag returneras) | +| OpenAI API | Embeddingar for likhetsmatchning | erp-base -> OpenAI -> erp-base (transaktionsbeskrivningar skickas, vektorer returneras) | +| Resend | E-postutskick | erp-base -> Resend -> mottagare (fakturor, påminnelser) | + +[ANGE YTTERLIGARE INTEGRATIONER OM TILLÄMPLIGT] + +## 12. Behörigheter och åtkomstkontroll + +12.1. Varje konto i erp-base är isolerat via Row Level Security (RLS) i databasen. En användare kan enbart se och redigera sin egen data. + +12.2. Nuvarande behörighetsstruktur: + +| Roll | Beskrivning | +|---|---| +| Kontoägare | Full åtkomst till all data och funktionalitet | + +[OM YTTERLIGARE ROLLER FINNS, BESKRIV HÄR] + +12.3. Ansvarig for att tilldela och granska behörigheter: [NAMN] + +## 13. Uppdatering av systemdokumentationen + +Systemdokumentationen ska uppdateras vid: +- Byte eller uppgradering av bokföringsprogram +- Ändringar i kontoplan +- Ändringar i momshantering +- Nya integrationer eller delsystem +- Minst en gång per räkenskapsår + +| Datum | Ändring | Utförd av | +|---|---|---| +| [DATUM] | Första version upprättad | [NAMN] | +| | | | + +--- + +*Denna systemdokumentation uppfyller kraven i 5 kap. 11 § BFL och BFNAR 2013:2 kapitel 9 (punkterna 9.1-9.16) samt Exempel 9.1-9.4 i vägledningen. Anpassa innehållet till ditt företags specifika förhållanden.* diff --git a/public/gnubokiceon-removebg-preview.png b/public/gnubokiceon-removebg-preview.png new file mode 100644 index 0000000000000000000000000000000000000000..9126809b812349c5fb700efd805c99cd14a9d7ac GIT binary patch literal 108424 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≠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�o3svirTs5`gAIl>5O#5+y~x9k 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