Fix/company creation bug (#212)

* feat: enhance JournalEntryForm with currency selection and exchange rate fetching

- Added currency selection to JournalEntryForm, allowing users to choose from multiple currencies (SEK, EUR, USD, GBP, NOK, DKK).
- Implemented fetching of exchange rates from Riksbanken API based on selected currency and entry date.
- Updated calculations for foreign amounts and SEK equivalents based on user input and fetched exchange rates.
- Improved form handling to reset currency-related fields when switching back to SEK.

feat: refactor WelcomeOnboarding to streamline company creation process

- Replaced direct company switching with a new server action to create a company from onboarding data.
- Added validation for fiscal period during onboarding steps, allowing for mid-month starts for the first fiscal period.
- Enhanced error handling and rollback mechanisms to ensure data integrity during company creation.

fix: update Step3TaxRegistration to allow flexible first-year start dates

- Modified date selection to include day, month, and year for the first-year start date.
- Updated validation messages to reflect changes in fiscal year start date handling.

test: expand validate-period-duration tests for fiscal period validation

- Added tests to validate that mid-month starts are allowed for the first fiscal period.
- Ensured that subsequent periods must start on the 1st of the month and enforced maximum duration constraints.

feat: implement currency rate API endpoint

- Created a new API route to fetch exchange rates for specified currencies, ensuring user authentication.
- Validated currency input and handled errors for invalid requests.

chore: update database constraints for fiscal periods

- Modified database constraints to allow custom start dates for the first fiscal period while enforcing day-1 starts for subsequent periods.

* fix: implement computeFiscalPeriod function for onboarding and refactor JournalEntryForm

* Fixed date issue

* Added migration
This commit is contained in:
Mattsson
2026-04-10 11:02:28 +02:00
committed by GitHub
parent e8928b7885
commit aa405b9a74
14 changed files with 664 additions and 555 deletions
@@ -68,8 +68,18 @@ export async function PATCH(
const newStart = body.period_start || period.period_start
const newEnd = body.period_end || period.period_end
// First period for this company may start on any day (BFL 3 kap.)
const { count: earlierCount } = await supabase
.from('fiscal_periods')
.select('id', { count: 'exact', head: true })
.eq('company_id', companyId)
.neq('id', id)
.lt('period_start', newStart)
const isFirstPeriod = !earlierCount || earlierCount === 0
// Validate period duration (max 18 months per BFL 3 kap.)
const durationError = validatePeriodDuration(newStart, newEnd)
const durationError = validatePeriodDuration(newStart, newEnd, { isFirstPeriod })
if (durationError) {
return NextResponse.json({ error: durationError }, { status: 400 })
}
@@ -111,11 +121,11 @@ export async function PATCH(
.single()
if (updateError) {
// Database CHECK constraints will catch invalid month boundaries
// Database trigger/constraint will catch invalid month boundaries
const msg = updateError.message
if (msg.includes('period_start') || msg.includes('period_end')) {
if (msg.includes('period_start') || msg.includes('period_end') || msg.includes('first of a month') || msg.includes('1st of a month')) {
return NextResponse.json(
{ error: 'Perioden måste börja den 1:a i en månad och sluta sista dagen i en månad' },
{ error: 'Perioden måste sluta sista dagen i en månad. Efterföljande perioder måste börja den 1:a.' },
{ status: 400 }
)
}
+9 -6
View File
@@ -42,12 +42,6 @@ export async function POST(request: Request) {
if (!validation.success) return validation.response
const body = validation.data
// Validate period duration (max 18 months per BFL 3 kap.)
const durationError = validatePeriodDuration(body.period_start, body.period_end)
if (durationError) {
return NextResponse.json({ error: durationError }, { status: 400 })
}
// Enforce continuity: new period must chain from the latest existing period (BFL 3:1)
const { data: latest } = await supabase
.from('fiscal_periods')
@@ -57,6 +51,15 @@ export async function POST(request: Request) {
.limit(1)
.maybeSingle()
// First period for this company may start on any day (BFL 3 kap.)
const isFirstPeriod = !latest
// Validate period duration (max 18 months per BFL 3 kap.)
const durationError = validatePeriodDuration(body.period_start, body.period_end, { isFirstPeriod })
if (durationError) {
return NextResponse.json({ error: durationError }, { status: 400 })
}
if (latest) {
const prev = new Date(latest.period_end + 'T00:00:00')
prev.setDate(prev.getDate() + 1)
+31
View File
@@ -0,0 +1,31 @@
import { NextResponse } from 'next/server'
import { createClient } from '@/lib/supabase/server'
import { fetchExchangeRate } from '@/lib/currency/riksbanken'
import type { Currency } from '@/types'
const VALID_CURRENCIES: Currency[] = ['EUR', 'USD', 'GBP', 'NOK', 'DKK']
export async function GET(request: Request) {
const supabase = await createClient()
const { data: { user } } = await supabase.auth.getUser()
if (!user) {
return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })
}
const { searchParams } = new URL(request.url)
const currency = searchParams.get('currency') as Currency | null
const dateStr = searchParams.get('date')
if (!currency || !VALID_CURRENCIES.includes(currency)) {
return NextResponse.json({ error: 'Invalid currency' }, { status: 400 })
}
const date = dateStr ? new Date(dateStr) : undefined
const rate = await fetchExchangeRate(currency, date)
if (!rate) {
return NextResponse.json({ error: 'Could not fetch exchange rate' }, { status: 502 })
}
return NextResponse.json({ data: rate })
}