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:
@@ -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 }
|
||||
)
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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 })
|
||||
}
|
||||
Reference in New Issue
Block a user