fix: resolve INK2/NE entity_type from companies table fallback (#217)

* fix: resolve INK2/NE entity_type from companies table fallback (#193)

The entity_type check in INK2 and NE-bilaga engines read from
company_settings where it is nullable, causing "only for aktiebolag"
errors when the column is null. Now falls back to companies.entity_type
(NOT NULL, always set). Reports page uses useCompany() context instead
of /api/settings for tab visibility.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix: address Greptile review — surface fallback errors, avoid direct mutation

- Surface Supabase errors in entity_type fallback queries instead of
  silently swallowing them (ink2-engine, ne-engine)
- Use spread instead of direct mutation on Supabase result object
  (settings route)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix: use separate variable to avoid const reassignment in settings route

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Jakob Wennberg
2026-04-11 14:38:02 +02:00
committed by GitHub
co-authored by Claude Opus 4.6
parent bca00cc5bd
commit 73fb97052b
4 changed files with 45 additions and 21 deletions
+5 -16
View File
@@ -9,6 +9,7 @@ import { Badge } from '@/components/ui/badge'
import { Tabs, TabsList, TabsTrigger, TabsContent } from '@/components/ui/tabs'
import { Download, AlertCircle, ChevronDown, ChevronRight, ArrowRight } from 'lucide-react'
import { AccountNumber } from '@/components/ui/account-number'
import { useCompany } from '@/contexts/CompanyContext'
import { NEDeclarationView } from '@/components/reports/NEDeclarationView'
import { INK2DeclarationView } from '@/components/reports/INK2DeclarationView'
import { BankReconciliationView } from '@/components/reports/BankReconciliationView'
@@ -48,8 +49,8 @@ export default function ReportsPage() {
const [periods, setPeriods] = useState<FiscalPeriod[]>([])
const [selectedPeriod, setSelectedPeriod] = useState('')
const [activeTab, setActiveTab] = useState('trial-balance')
const [entityType, setEntityType] = useState<string | null>(null)
const [isLoadingInit, setIsLoadingInit] = useState(true)
const { company } = useCompany()
// Drill-down state: when navigating from a report to the GL for a specific account
const [glAccountFilter, setGlAccountFilter] = useState<string | null>(null)
@@ -89,26 +90,14 @@ export default function ReportsPage() {
}
}
async function fetchEntityType() {
try {
const res = await fetch('/api/settings')
const { data } = await res.json()
if (data?.entity_type) {
setEntityType(data.entity_type)
}
} catch {
// Ignore - entity type is optional for tab visibility
}
}
useEffect(() => {
Promise.all([fetchPeriods(), fetchEntityType()]).finally(() => {
fetchPeriods().finally(() => {
setIsLoadingInit(false)
})
}, [])
const isEnskildFirma = entityType === 'enskild_firma'
const isAktiebolag = entityType === 'aktiebolag'
const isEnskildFirma = company?.entity_type === 'enskild_firma'
const isAktiebolag = company?.entity_type === 'aktiebolag'
return (
<div className="space-y-6">
+14 -1
View File
@@ -26,7 +26,20 @@ export async function GET() {
return NextResponse.json({ error: error.message }, { status: 500 })
}
return NextResponse.json({ data })
// Fall back to companies.entity_type if company_settings.entity_type is null
let responseData = data
if (data && !data.entity_type) {
const { data: company } = await supabase
.from('companies')
.select('entity_type')
.eq('id', companyId)
.single()
if (company?.entity_type) {
responseData = { ...data, entity_type: company.entity_type }
}
}
return NextResponse.json({ data: responseData })
}
export async function PUT(request: Request) {
+13 -2
View File
@@ -714,8 +714,19 @@ export async function generateINK2Declaration(
.eq('company_id', companyId)
.single()
// Validate entity type
if (settings?.entity_type !== 'aktiebolag') {
// Resolve entity_type: prefer company_settings, fall back to companies table (NOT NULL, always reliable)
let entityType = settings?.entity_type
if (!entityType) {
const { data: company, error: companyError } = await supabase
.from('companies')
.select('entity_type')
.eq('id', companyId)
.single()
if (companyError) throw new Error(`Failed to resolve entity type: ${companyError.message}`)
entityType = company?.entity_type
}
if (entityType !== 'aktiebolag') {
throw new Error('INK2 declaration is only for aktiebolag (limited company)')
}
+13 -2
View File
@@ -179,8 +179,19 @@ export async function generateNEDeclaration(
.eq('company_id', companyId)
.single()
// Check if it's enskild firma
if (settings?.entity_type !== 'enskild_firma') {
// Resolve entity_type: prefer company_settings, fall back to companies table (NOT NULL, always reliable)
let entityType = settings?.entity_type
if (!entityType) {
const { data: company, error: companyError } = await supabase
.from('companies')
.select('entity_type')
.eq('id', companyId)
.single()
if (companyError) throw new Error(`Failed to resolve entity type: ${companyError.message}`)
entityType = company?.entity_type
}
if (entityType !== 'enskild_firma') {
throw new Error('NE declaration is only for enskild firma (sole proprietorship)')
}