Files
accounted/app/api/reports/vat-declaration/route.ts
T
Jakob Wennberg 03b569d708 refactor: consolidate extension system to general-only with manifest-driven architecture
- Remove all sector-specific extensions (construction, ecommerce, export,
  hotel, restaurant, tech) — only general-purpose extensions remain
- Move NE-bilaga and SRU export from extensions to core reports (lib/reports/)
- Move moms-box-mapping from extensions/export/shared to lib/vat/
- Replace per-extension API routes with catch-all dispatcher
  (app/api/extensions/ext/[...path]/route.ts)
- Add manifest.json for each extension with metadata, env vars, and deps
- Add api-routes.ts pattern for extension-defined API endpoints
- Add code generation scripts (generate-extension-registry, create-extension)
- Add extensions.config.json for opt-in extension loading
- Add extensions.schema.json for config validation
- Add email service interface with noop default (lib/email/service.ts)
- Add CI workflow (core-build.yml) to verify core builds with zero extensions
- Add migration 045: expand account_type CHECK for untaxed_reserves
- Update CLAUDE.md with comprehensive extension system documentation
- Update all report engines and bookkeeping services for new imports
- Clean up extensions.schema.json to only list existing extensions

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-26 14:32:56 +01:00

126 lines
3.3 KiB
TypeScript

import { createClient } from '@/lib/supabase/server'
import { NextResponse } from 'next/server'
import {
calculateVatDeclaration,
formatPeriodLabel,
} from '@/lib/reports/vat-declaration'
import type { VatPeriodType, AccountingMethod } from '@/types'
/**
* GET /api/reports/vat-declaration
*
* Calculate VAT declaration (momsdeklaration) for a given period.
*
* Query parameters:
* - periodType: 'monthly' | 'quarterly' | 'yearly'
* - year: number (e.g., 2025)
* - period: number (1-12 for monthly, 1-4 for quarterly, 1 for yearly)
*
* Returns:
* - VAT rutor (boxes) according to Swedish tax authority format
* - Period information
* - Breakdown by source (invoices, transactions, receipts)
*/
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 periodType = searchParams.get('periodType') as VatPeriodType | null
const yearStr = searchParams.get('year')
const periodStr = searchParams.get('period')
// Validate required parameters
if (!periodType || !yearStr || !periodStr) {
return NextResponse.json(
{ error: 'Missing required parameters: periodType, year, period' },
{ status: 400 }
)
}
// Validate periodType
if (!['monthly', 'quarterly', 'yearly'].includes(periodType)) {
return NextResponse.json(
{ error: 'Invalid periodType. Must be: monthly, quarterly, or yearly' },
{ status: 400 }
)
}
const year = parseInt(yearStr, 10)
const period = parseInt(periodStr, 10)
// Validate year
if (isNaN(year) || year < 2000 || year > 2100) {
return NextResponse.json(
{ error: 'Invalid year. Must be between 2000 and 2100' },
{ status: 400 }
)
}
// Validate period based on type
if (isNaN(period)) {
return NextResponse.json(
{ error: 'Invalid period' },
{ status: 400 }
)
}
if (periodType === 'monthly' && (period < 1 || period > 12)) {
return NextResponse.json(
{ error: 'Invalid period for monthly. Must be 1-12' },
{ status: 400 }
)
}
if (periodType === 'quarterly' && (period < 1 || period > 4)) {
return NextResponse.json(
{ error: 'Invalid period for quarterly. Must be 1-4' },
{ status: 400 }
)
}
if (periodType === 'yearly' && period !== 1) {
return NextResponse.json(
{ error: 'Invalid period for yearly. Must be 1' },
{ status: 400 }
)
}
// Fetch accounting method
const { data: settings } = await supabase
.from('company_settings')
.select('accounting_method')
.eq('user_id', user.id)
.single()
const accountingMethod = (settings?.accounting_method as AccountingMethod) || 'accrual'
try {
const declaration = await calculateVatDeclaration(
supabase,
user.id,
periodType,
year,
period,
accountingMethod
)
return NextResponse.json({
data: {
...declaration,
periodLabel: formatPeriodLabel(periodType, year, period),
},
})
} catch (err) {
console.error('Error calculating VAT declaration:', err)
return NextResponse.json(
{ error: err instanceof Error ? err.message : 'Failed to calculate VAT declaration' },
{ status: 500 }
)
}
}