From 08e1757798541d70d0be63d38f6422362b99b213 Mon Sep 17 00:00:00 2001 From: Mattsson <111893710+mattssonn@users.noreply.github.com> Date: Wed, 18 Mar 2026 16:10:17 +0100 Subject: [PATCH] Onboarding updates (#54) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Removed månatlig skatt from onboarding flow * fix: convert DB null to undefined for moms_period in onboarding step 3 The moms_period field used a type cast (`as MomsPeriod | undefined`) instead of null coalescing (`?? undefined`), causing Zod validation to reject the DB-default null value since `.optional()` only accepts undefined. Co-Authored-By: Claude Opus 4.6 * feat: add VAT accounting step to onboarding process * feat: replace accounting method select with checkboxes for better UX --------- Co-authored-by: Claude Opus 4.6 --- app/(onboarding)/onboarding/page.tsx | 21 +- .../onboarding/Step3TaxRegistration.tsx | 196 +---------- components/onboarding/Step4VatAccounting.tsx | 304 ++++++++++++++++++ 3 files changed, 319 insertions(+), 202 deletions(-) create mode 100644 components/onboarding/Step4VatAccounting.tsx diff --git a/app/(onboarding)/onboarding/page.tsx b/app/(onboarding)/onboarding/page.tsx index 810f806b..9a632da0 100644 --- a/app/(onboarding)/onboarding/page.tsx +++ b/app/(onboarding)/onboarding/page.tsx @@ -14,14 +14,14 @@ import type { CompanySettings, EntityType, MomsPeriod } from '@/types' import Step1EntityType from '@/components/onboarding/Step1EntityType' import Step2CompanyDetails from '@/components/onboarding/Step2CompanyDetails' import Step3TaxRegistration from '@/components/onboarding/Step3TaxRegistration' -import Step4PreliminaryTax from '@/components/onboarding/Step4PreliminaryTax' +import Step4VatAccounting from '@/components/onboarding/Step4VatAccounting' import Step5ConnectBank from '@/components/onboarding/Step6ConnectBank' 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: 'F-skatt & räkenskapsår', subtitle: 'Ange din skatteregistrering och räkenskapsår.', label: 'Skatt' }, + { title: 'Moms & bokföring', subtitle: 'Momsregistrering och bokföringsmetod.', label: 'Moms' }, { title: 'Bankuppgifter', subtitle: 'Dessa visas på dina fakturor.', label: 'Bank' }, ] @@ -471,13 +471,8 @@ function OnboardingPageContent() { initialData={{ f_skatt: settings.f_skatt ?? undefined, fiscal_year_start_month: settings.fiscal_year_start_month ?? undefined, - vat_registered: settings.vat_registered ?? undefined, - vat_number: settings.vat_number ?? undefined, - moms_period: settings.moms_period as MomsPeriod | undefined, - accounting_method: (settings.accounting_method as 'accrual' | 'cash') ?? undefined, }} entityType={settings.entity_type as EntityType} - orgNumber={settings.org_number ?? undefined} onNext={(data) => handleNext(data)} onBack={handleBack} isSaving={isSaving} @@ -485,13 +480,17 @@ function OnboardingPageContent() { )} {currentStep === 4 && ( - handleNext(data)} onBack={handleBack} - onSkip={handleSkip} isSaving={isSaving} /> )} diff --git a/components/onboarding/Step3TaxRegistration.tsx b/components/onboarding/Step3TaxRegistration.tsx index 3b3dab7a..3aa0e6a1 100644 --- a/components/onboarding/Step3TaxRegistration.tsx +++ b/components/onboarding/Step3TaxRegistration.tsx @@ -1,21 +1,20 @@ 'use client' -import { useState, useMemo, useEffect } from 'react' +import { useState, useMemo } from 'react' import { useForm, Controller } from 'react-hook-form' import { zodResolver } from '@hookform/resolvers/zod' import { z } from 'zod' import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card' import { Button } from '@/components/ui/button' -import { Input } from '@/components/ui/input' import { Label } from '@/components/ui/label' import { Checkbox } from '@/components/ui/checkbox' import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/components/ui/select' import { InfoTooltip } from '@/components/ui/info-tooltip' -import { Loader2, ArrowRight, ArrowLeft, Check, CalendarDays, Info } from 'lucide-react' +import { Loader2, ArrowRight, ArrowLeft, Check, CalendarDays } from 'lucide-react' import { cn } from '@/lib/utils' import { useToast } from '@/components/ui/use-toast' import { monthsBetween } from '@/lib/bookkeeping/validate-period-duration' -import type { MomsPeriod, EntityType } from '@/types' +import type { EntityType } from '@/types' const schema = z.object({ f_skatt: z.boolean(), @@ -25,11 +24,6 @@ const schema = z.object({ first_year_end: z.string().optional(), // Ongoing year field (conditional) fiscal_year_end_month: z.number().min(1).max(12).optional(), - // Existing fields - vat_registered: z.boolean(), - vat_number: z.string().optional(), - moms_period: z.enum(['monthly', 'quarterly', 'yearly']).optional(), - accounting_method: z.enum(['accrual', 'cash']), }).superRefine((data, ctx) => { if (data.is_first_fiscal_year) { if (!data.first_year_start) { @@ -47,13 +41,6 @@ const schema = z.object({ }) } } - if (data.vat_registered && !data.moms_period) { - ctx.addIssue({ - code: z.ZodIssueCode.custom, - message: 'Välj momsredovisningsperiod.', - path: ['moms_period'], - }) - } }) type FormData = z.infer @@ -65,16 +52,11 @@ interface Step3Output { is_first_fiscal_year: boolean first_year_start?: string first_year_end?: string - vat_registered: boolean - vat_number?: string - moms_period?: MomsPeriod - accounting_method: 'accrual' | 'cash' } interface Step3Props { initialData: Partial entityType?: EntityType - orgNumber?: string onNext: (data: Step3Output) => void onBack: () => void isSaving: boolean @@ -164,7 +146,6 @@ function getABFirstYearEndDates( export default function Step3TaxRegistration({ initialData, entityType, - orgNumber, onNext, onBack, isSaving, @@ -173,11 +154,9 @@ export default function Step3TaxRegistration({ const { toast } = useToast() const { - register, handleSubmit, watch, control, - setValue, formState: { errors }, } = useForm({ resolver: zodResolver(schema), @@ -190,30 +169,13 @@ export default function Step3TaxRegistration({ fiscal_year_end_month: initialData.fiscal_year_start_month ? (initialData.fiscal_year_start_month === 1 ? 12 : initialData.fiscal_year_start_month - 1) : 12, - vat_registered: initialData.vat_registered ?? false, - vat_number: initialData.vat_number || '', - moms_period: initialData.moms_period, - accounting_method: initialData.accounting_method ?? 'accrual', }, }) - const vatRegistered = watch('vat_registered') const isFirstYear = watch('is_first_fiscal_year') const firstYearStart = watch('first_year_start') const firstYearEnd = watch('first_year_end') const fiscalYearEndMonth = watch('fiscal_year_end_month') - const accountingMethod = watch('accounting_method') - - // Auto-fill VAT number when vat_registered toggles on - const vatNumber = watch('vat_number') - useEffect(() => { - if (vatRegistered && !vatNumber && orgNumber) { - const cleaned = orgNumber.replace(/[-\s]/g, '') - if (cleaned.length >= 10) { - setValue('vat_number', `SE${cleaned}01`) - } - } - }, [vatRegistered, vatNumber, orgNumber, setValue]) // State for first-year start date selectors (month/year) const [startMonth, setStartMonth] = useState( @@ -278,10 +240,6 @@ export default function Step3TaxRegistration({ is_first_fiscal_year: data.is_first_fiscal_year, ...(firstStart && { first_year_start: firstStart }), ...(firstEnd && { first_year_end: firstEnd }), - vat_registered: data.vat_registered, - vat_number: data.vat_registered ? data.vat_number : undefined, - moms_period: data.vat_registered ? data.moms_period : undefined, - accounting_method: data.accounting_method, } onNext(output) @@ -611,161 +569,17 @@ export default function Step3TaxRegistration({ )} - {/* VAT section */} -
-

- -

Behöver jag momsregistrera mig?

-

Ja, om din omsättning överstiger 120 000 kr per år. Med moms lägger du på 25% extra på dina fakturor, men får också dra av moms på dina inköp.

-

Om din omsättning överstiger 120 000 kr per år behöver du momsregistrera dig.

-

- } - side="right" - > - Momsregistrering - - - -
- ( - - )} - /> -
- -

- Obligatoriskt om din omsättning överstiger 120 000 kr per år. -

-
-
- - {vatRegistered && ( -
-
- - -

- Format: SE + organisationsnummer + 01 -

-
- -
- -

Hur ofta rapporterar du moms?

-

Välj den period som anges på Verksamt eller i ditt beslut från Skatteverket.

-
    -
  • Under 1 miljon/år = Kan välja årsredovisning
  • -
  • 1-40 miljoner = Kvartal
  • -
  • Över 40 miljoner = Månad
  • -
-
- } - side="right" - > - - - ( - - )} - /> -

- Välj den period som anges i ditt beslut från Skatteverket. Vanligtvis kvartal eller år. -

-
- - )} - - - {/* Accounting method */} -
-
- - - - ( - - )} - /> -
-
- - {accountingMethod === 'accrual' ? 'Faktureringsmetoden' : 'Kontantmetoden'} -
-

- {accountingMethod === 'accrual' - ? 'Intäkter och kostnader bokförs när fakturan skickas eller tas emot, oavsett när betalningen sker. Detta ger en mer rättvisande bild av verksamhetens ekonomi.' - : 'Intäkter och kostnader bokförs först när betalningen faktiskt sker. Enklare att hantera men ger en mindre exakt bild av verksamhetens ekonomi vid varje given tidpunkt.'} -

- {entityType === 'aktiebolag' && ( -

- Aktiebolag med omsättning över 3 MSEK per år måste använda faktureringsmetoden. -

- )} -
-
-
- -
+
-
+ + {/* Accounting method */} +
+
+ + + + ( +
+
+ { if (checked) field.onChange('accrual') }} + /> + +
+
+ { if (checked) field.onChange('cash') }} + /> + +
+
+ )} + /> +
+
+ + {accountingMethod === 'accrual' ? 'Faktureringsmetoden' : 'Kontantmetoden'} +
+

+ {accountingMethod === 'accrual' + ? 'Intäkter och kostnader bokförs när fakturan skickas eller tas emot, oavsett när betalningen sker. Detta ger en mer rättvisande bild av verksamhetens ekonomi.' + : 'Intäkter och kostnader bokförs först när betalningen faktiskt sker. Enklare att hantera men ger en mindre exakt bild av verksamhetens ekonomi vid varje given tidpunkt.'} +

+ {entityType === 'aktiebolag' && ( +

+ Aktiebolag med omsättning över 3 MSEK per år måste använda faktureringsmetoden. +

+ )} +
+
+
+ +
+ + +
+ + + +
+ ) +}