Refactor to generic ERP base

Remove influencer-specific features (campaigns, TikTok, gifts, shadow ledger,
contracts, briefings) and consolidate into a clean ERP foundation with core
bookkeeping, invoicing, receipts, tax reporting, and calendar functionality.
Reorganize database migrations into a clean numbered sequence.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Jakob Wennberg
2026-02-13 15:39:13 +01:00
co-authored by Claude Opus 4.6
parent a25d10a528
commit 0aecf5b41c
184 changed files with 1418 additions and 24763 deletions
-78
View File
@@ -3,12 +3,9 @@ import type {
NEDeclaration,
NEDeclarationRutor,
NEAccountMapping,
NEGiftBreakdown,
NEGiftItem,
FiscalPeriod,
JournalEntry,
JournalEntryLine,
Gift,
} from '@/types'
/**
@@ -295,67 +292,6 @@ export async function generateNEDeclaration(
rutor.R11 = totalRevenue - totalExpenses
breakdown.R11.total = rutor.R11
// Fetch gifts for the period
const { data: gifts } = await supabase
.from('gifts')
.select('*')
.eq('user_id', userId)
.gte('date', period.period_start)
.lte('date', period.period_end)
// Build gift breakdown
let giftBreakdown: NEGiftBreakdown | undefined
if (gifts && gifts.length > 0) {
const giftItems: NEGiftItem[] = []
let r1Total = 0
let r2Total = 0
let r6Total = 0
for (const gift of gifts as Gift[]) {
const classification = gift.classification
if (!classification || !classification.taxable) continue
const item: NEGiftItem = {
id: gift.id,
date: gift.date,
brandName: gift.brand_name,
description: gift.description,
marketValue: gift.estimated_value,
vatLiable: classification.vatLiable,
vatAmount: classification.vatAmount,
deductibleAsExpense: classification.deductibleAsExpense,
neIncomeRuta: classification.neIncomeRuta,
neExpenseRuta: classification.neExpenseRuta,
}
giftItems.push(item)
// Accumulate totals based on NE ruta
if (classification.neIncomeRuta === 'R1') {
r1Total += classification.valueExclVat // Exkl moms för R1
} else if (classification.neIncomeRuta === 'R2') {
r2Total += gift.estimated_value
}
if (classification.neExpenseRuta === 'R6') {
r6Total += gift.estimated_value
}
}
if (giftItems.length > 0) {
giftBreakdown = {
gifts: giftItems,
summary: {
r1Total: roundToKrona(r1Total),
r2Total: roundToKrona(r2Total),
r6Total: roundToKrona(r6Total),
totalTaxable: roundToKrona(r1Total + r2Total),
totalDeductible: roundToKrona(r6Total),
netTaxableIncome: roundToKrona(r1Total + r2Total - r6Total),
},
}
}
}
// Add warnings
if (!(period as FiscalPeriod).is_closed) {
warnings.push('Räkenskapsåret är inte stängt. Siffrorna kan ändras.')
@@ -365,19 +301,6 @@ export async function generateNEDeclaration(
warnings.push('Inga bokförda intäkter eller kostnader hittades för perioden.')
}
// Check for gifts without journal entries
if (giftBreakdown && giftBreakdown.gifts.length > 0) {
const giftsWithoutEntries = (gifts as Gift[]).filter(
(g) => g.classification?.taxable && !g.journal_entry_id
)
if (giftsWithoutEntries.length > 0) {
warnings.push(
`${giftsWithoutEntries.length} skattepliktiga gåvor saknar bokföringsverifikation. ` +
'Dessa bör bokföras för att inkluderas i NE-bilagan.'
)
}
}
return {
fiscalYear: {
id: period.id,
@@ -388,7 +311,6 @@ export async function generateNEDeclaration(
},
rutor,
breakdown,
giftBreakdown,
companyInfo: {
companyName: settings?.company_name || 'Okänt företag',
orgNumber: settings?.org_number || null,
+1 -1
View File
@@ -52,7 +52,7 @@ export async function generateSIEExport(
lines.push('#FLAGGA 0')
lines.push('#FORMAT PC8')
lines.push('#SIETYP 4')
lines.push(`#PROGRAM "${options.program_name || 'InfluencerBiz'}" "1.0"`)
lines.push(`#PROGRAM "${options.program_name || 'ERPBase'}" "1.0"`)
lines.push(`#GEN ${formatSIEDate(now)}`)
if (options.org_number) {
+1 -1
View File
@@ -54,7 +54,7 @@ export function generateSRUFile(declaration: NEDeclaration): SRUFile {
// File header
records.push({ fieldCode: 'PRODUKT', value: 'KONTROLLUPPGIFTER' })
records.push({ fieldCode: 'SESSION', value: '1' })
records.push({ fieldCode: 'PROGRAMNAMN', value: 'InfluencerBiz' })
records.push({ fieldCode: 'PROGRAMNAMN', value: 'ERPBase' })
records.push({ fieldCode: 'PROGRAMVERSION', value: '1.0' })
records.push({
fieldCode: 'SKAPAT',
-2
View File
@@ -328,8 +328,6 @@ function getVatRateForCategory(category: string | null): number {
'uncategorized',
'income_services',
'income_products',
'income_sponsorship',
'income_affiliate',
'income_other',
]