books/backend/Books.Api/EventFlow/Subscribers/StandardDanishAccounts.cs

232 lines
17 KiB
C#
Raw Normal View History

using Books.Api.Domain.Accounts;
namespace Books.Api.EventFlow.Subscribers;
/// <summary>
/// Standard Danish chart of accounts following Danish accounting standards.
/// Account number ranges:
/// - 1xxx: Revenue (Omsætning) + Current Assets (Omsætningsaktiver)
/// - 17xx: Fixed Assets (Anlægsaktiver)
/// - 2xxx: Cost of Goods Sold (Vareforbrug)
/// - 3xxx: Equity (Egenkapital) - ONLY equity accounts per Danish standards
/// - 4xxx: Sales Expenses (Salgsomkostninger)
/// - 5xxx: Location Costs (Lokaleomkostninger) + Personnel (Personaleomkostninger)
/// - 6xxx: Vehicle/Travel Expenses (Kørselsomkostninger)
/// - 7xxx: Administrative Expenses (Administrationsomkostninger) + Liabilities
/// - 8xxx: Depreciation (Afskrivninger)
/// - 9xxx: Financial Items (Finansielle poster)
///
/// StandardAccountNumber refers to Erhvervsstyrelsens standardkontoplan for SAF-T compliance.
/// Mapping based on official SAF-T 2.0 specification (mandatory from Jan 2027).
/// </summary>
public static class StandardDanishAccounts
{
public record StandardAccount(
string AccountNumber,
string Name,
AccountType AccountType,
string? Description,
string? DefaultVatCode,
string? StandardAccountNumber,
bool IsSystemAccount = false);
public static IEnumerable<StandardAccount> GetAll()
{
// =========================================
// OMSÆTNING (Revenue) - 1xxx
// Standard: 1010 = Salg af varer og ydelser med moms
// =========================================
yield return new("1000", "Salg af varer, DK", AccountType.Revenue, "Salg med 25% moms", "U25", "1010", true);
yield return new("1010", "Salg af varer, momsfrit", AccountType.Revenue, "Momsfrit salg", null, "1010", true);
yield return new("1050", "Salg af varer, EU", AccountType.Revenue, "EU-salg af varer", "UEU", "1050", true);
yield return new("1100", "Salg af varer, verden", AccountType.Revenue, "Eksport uden for EU", "UEXP", "1100", true);
yield return new("1200", "Salg af ydelser, DK", AccountType.Revenue, "Salg med 25% moms", "U25", "1010", true);
yield return new("1250", "Salg af ydelser, EU", AccountType.Revenue, "EU-salg af ydelser", "UEU", "1210", true);
yield return new("1300", "Salg af ydelser, verden", AccountType.Revenue, "Eksport uden for EU", "UEXP", "1310", true);
yield return new("1400", "Øvrig omsætning", AccountType.Revenue, null, "U25", "1510");
yield return new("1500", "Valutakursdifferencer, salg", AccountType.Revenue, null, null, "1510");
// =========================================
// ANLÆGSAKTIVER (Fixed Assets) - 17xx
// Standard: 5530 = Grunde og bygninger, 5570 = Produktionsanlæg
// =========================================
yield return new("1710", "Bygninger", AccountType.Asset, "Ejendomme og bygninger", null, "5530");
yield return new("1720", "Maskiner og inventar", AccountType.Asset, "Driftsmidler og maskiner", null, "5570");
yield return new("1730", "Køretøjer", AccountType.Asset, "Biler og transportmidler", null, "5570");
// =========================================
// OMSÆTNINGSAKTIVER (Current Assets) - 18xx-19xx
// Standard: 6140 = Varebeholdninger, 6190 = Tilgodehavender fra salg
// =========================================
yield return new("1800", "Varelager", AccountType.Asset, null, null, "6140");
yield return new("1850", "Igangværende arbejder", AccountType.Asset, null, null, "6150");
yield return new("1900", "Debitorer", AccountType.Asset, "Tilgodehavender fra salg", null, "6190", true);
yield return new("1950", "Andre tilgodehavender", AccountType.Asset, null, null, "6300");
// =========================================
// LIKVIDE BEHOLDNINGER (Cash & Bank) - 197x-198x
// Standard: 6470 = Kontanter, 6480 = Bankindeståender
// =========================================
yield return new("1970", "Bankkonto", AccountType.Asset, "Bankkonto til Open Banking", null, "6480");
// =========================================
// VAREFORBRUG (COGS) - 2xxx
// Standard: 1610 = Anvendt råvarer, 2070 = Køb af ydelser
// =========================================
yield return new("2000", "Vareforbrug", AccountType.Cogs, "Køb af varer til videresalg", "I25", "1610");
yield return new("2050", "EU-erhvervelser varer", AccountType.Cogs, "Varekøb fra EU-lande", "IEUV", "1610");
yield return new("2100", "EU-erhvervelser ydelser", AccountType.Cogs, "Ydelseskøb fra EU-lande", "IEUY", "2070");
yield return new("2150", "Varekøb verden", AccountType.Cogs, "Varekøb fra lande uden for EU", "IVV", "1610");
yield return new("2200", "Ydelseskøb verden", AccountType.Cogs, "Ydelseskøb fra lande uden for EU", "IVY", "2070");
yield return new("2250", "Fragt med moms", AccountType.Cogs, "Fragtomkostninger med moms", "I25", "1810");
yield return new("2300", "Fragt uden moms", AccountType.Cogs, "Fragtomkostninger uden moms", null, "1810");
yield return new("2400", "Valutakursdifferencer, import", AccountType.Cogs, null, null, "3690");
yield return new("2450", "Varelagerregulering", AccountType.Cogs, null, null, "1640");
yield return new("2800", "Fremmed arbejde", AccountType.Cogs, "Underleverandører og freelancere", "I25", "2010");
// =========================================
// EGENKAPITAL (Equity) - 3xxx
// Standard: 6510 = Virksomhedskapital, 6935 = Overført resultat
// Per Danish accounting standards, 3xxx should ONLY contain equity accounts
// =========================================
yield return new("3000", "Aktiekapital/Anpartskapital", AccountType.Equity, "Indskudt kapital", null, "6510", true);
yield return new("3100", "Overkurs ved emission", AccountType.Equity, "Share premium", null, "6530");
yield return new("3900", "Overført resultat", AccountType.Equity, "Akkumuleret resultat", null, "6935", true);
yield return new("3910", "Årets resultat", AccountType.Equity, "Resultat for indeværende år", null, "6940", true);
// =========================================
// SALGSOMKOSTNINGER (Sales Expenses) - 4xxx
// Standard: 2170 = Reklame, 2180 = Rejser og møder
// =========================================
yield return new("4000", "Annoncer og reklame", AccountType.Expense, "Marketing og annoncering", "I25", "2170");
yield return new("4040", "Hotel, personale", AccountType.Expense, "Fuldt fradrag", "I25", "2180");
yield return new("4060", "Hotel, forretningsforbindelser", AccountType.Expense, "Delvis fradrag", "I25", "2180");
yield return new("4080", "Konferencer", AccountType.Expense, null, null, "2180");
yield return new("4100", "Messer", AccountType.Expense, null, "I25", "2170");
yield return new("4120", "Repræsentation, restaurant, personale", AccountType.Expense, "Fuldt fradrag", "REP", "2190");
yield return new("4140", "Repræsentation, restaurant, forretningsforbindelser", AccountType.Expense, "Delvis fradrag", "REP", "2190");
yield return new("4180", "Repræsentation, gaver og blomster", AccountType.Expense, "Delvis fradrag", null, "2190");
yield return new("4280", "Rejseomkostninger", AccountType.Expense, null, null, "2180");
// =========================================
// LOKALEOMKOSTNINGER (Location Costs) - 50xx
// Standard: 2150 = Lokaleomkostninger
// =========================================
yield return new("5000", "Husleje", AccountType.Expense, "Husleje med moms", "I25", "2150");
yield return new("5010", "Husleje uden moms", AccountType.Expense, "Husleje uden moms", null, "2150");
yield return new("5025", "El", AccountType.Expense, null, "I25", "2150");
yield return new("5030", "Vand", AccountType.Expense, null, "I25", "2150");
yield return new("5035", "Varme", AccountType.Expense, null, "I25", "2150");
yield return new("5040", "Elafgift", AccountType.Expense, null, null, "2150");
yield return new("5060", "Rengøring og affaldshåndtering", AccountType.Expense, null, "I25", "2150");
yield return new("5080", "Reparation og vedligeholdelse", AccountType.Expense, null, "I25", "2150");
yield return new("5100", "Ejendomsskat", AccountType.Expense, null, null, "2150");
// =========================================
// PERSONALEOMKOSTNINGER (Personnel) - 53xx
// Standard: 2310 = Lønninger, 2330 = Pensioner
// Moved from 3xxx to comply with Danish standards (3xxx = Equity only)
// =========================================
yield return new("5300", "AM-indkomst", AccountType.Personnel, "Løn til ansatte", null, "2310");
yield return new("5310", "Arbejdsgiver ATP", AccountType.Personnel, null, null, "2350");
yield return new("5320", "Medarbejder ATP", AccountType.Personnel, null, null, "2350");
yield return new("5330", "Sygepenge mv.", AccountType.Personnel, null, null, "2390");
yield return new("5340", "Personalegoder", AccountType.Personnel, "Herunder fri telefon", null, "2390");
yield return new("5350", "B-honorar", AccountType.Personnel, null, null, "2310");
yield return new("5360", "Barsel", AccountType.Personnel, null, null, "2390");
yield return new("5370", "Feriepenge og SH", AccountType.Personnel, null, null, "2310");
yield return new("5380", "Pension", AccountType.Personnel, null, null, "2330");
yield return new("5390", "Diæter/rejsegodtgørelse", AccountType.Personnel, null, null, "2310");
yield return new("5400", "Kørsel i egen bil", AccountType.Personnel, "Kilometergodtgørelse", null, "2140");
yield return new("5410", "AER/AES/ATP-finansieringsbidrag", AccountType.Personnel, null, null, "2350");
yield return new("5420", "Arbejdstøj", AccountType.Personnel, null, "I25", "2390");
yield return new("5430", "Mad under kursus/møder", AccountType.Personnel, "Fuldt fradrag", "I25", "2180");
yield return new("5440", "Gaver til personalet", AccountType.Personnel, "Fuldt fradrag", null, "2390");
yield return new("5450", "Uddannelsesudgifter", AccountType.Personnel, null, "I25", "2180");
yield return new("5460", "Regulering feriepenge", AccountType.Personnel, null, null, "2310");
// =========================================
// KØRSELSOMKOSTNINGER (Vehicle/Travel) - 6xxx
// Standard: 2140 = Bilomkostninger
// =========================================
yield return new("6000", "Billeje (gulplade)", AccountType.Expense, null, "I25", "2140");
yield return new("6020", "Brændstof (gulplade)", AccountType.Expense, null, "I25", "2140");
yield return new("6040", "Vedligeholdelse af bil", AccountType.Expense, null, "I25", "2140");
yield return new("6060", "Vægtafgift og forsikringer", AccountType.Expense, null, null, "2140");
yield return new("6080", "Parkering", AccountType.Expense, null, "I25", "2140");
yield return new("6100", "Broafgift", AccountType.Expense, null, "I25", "2140");
yield return new("6120", "Taxa", AccountType.Expense, null, null, "2140");
yield return new("6140", "Tog", AccountType.Expense, null, null, "2180");
yield return new("6160", "Fly", AccountType.Expense, null, null, "2180");
// =========================================
// PASSIVER - KREDITORER (Liabilities) - 69xx
// Standard: 7350 = Leverandører af varer og tjenesteydelser
// =========================================
yield return new("6900", "Kreditorer", AccountType.Liability, "Leverandørgæld", null, "7350", true);
yield return new("6950", "Anden gæld", AccountType.Liability, null, null, "7790");
// =========================================
// ADMINISTRATIONSOMKOSTNINGER (Admin Expenses) - 7xxx
// Standard: 2110 = Administrationsomkostninger
// =========================================
yield return new("7005", "Revision og regnskabsmæssig assistance", AccountType.Expense, null, "I25", "2110");
yield return new("7010", "Advokat", AccountType.Expense, null, "I25", "2110");
yield return new("7020", "Bogføringsassistance", AccountType.Expense, null, "I25", "2110");
yield return new("7040", "Konsulentbistand", AccountType.Expense, null, "I25", "2110");
yield return new("7060", "Kontingenter inkl. moms", AccountType.Expense, null, "I25", "2110");
yield return new("7080", "Kontingenter ekskl. moms", AccountType.Expense, null, null, "2110");
yield return new("7100", "Aviser", AccountType.Expense, null, null, "2110");
yield return new("7120", "Faglitteratur", AccountType.Expense, null, "I25", "2110");
yield return new("7160", "Erhvervsforsikringer", AccountType.Expense, null, null, "2110");
yield return new("7200", "Kontorartikler og tryksager", AccountType.Expense, null, "I25", "2110");
yield return new("7220", "Porto og gebyrer", AccountType.Expense, null, null, "2110");
yield return new("7240", "Telefoni", AccountType.Expense, null, "I25", "2110");
yield return new("7300", "Internet og webhotel", AccountType.Expense, null, "I25", "2110");
yield return new("7320", "Køb af software", AccountType.Expense, "SaaS og licenser", "I25", "2110");
yield return new("7360", "Offentlige bøder og gebyrer", AccountType.Expense, null, null, "2110");
yield return new("7400", "Betalingsløsning", AccountType.Expense, "Stripe, MobilePay mv.", "I25", "2110");
yield return new("7420", "Indløsere", AccountType.Expense, "Nets, Clearhaus mv.", null, "2110");
yield return new("7460", "Diverse inkl. moms", AccountType.Expense, null, "I25", "2110");
yield return new("7480", "Diverse ekskl. moms", AccountType.Expense, null, null, "2110");
Audit v2: fix security, data integrity, compliance, bugs, encoding, UX Backend Security & Data Integrity: - Block negative debit/credit amounts that bypass balance validation - Require document date at posting (was optional, bypassing fiscal year checks) - Fix event sourcing anti-pattern: timestamps now stored in events, not UtcNow in Apply - Add [Authorize] to BankingController OAuth callback - Add company access check on attachment downloads - Validate CVR in CompanyAggregate.Create and CompanyAggregate.Update - Require company CVR for invoice creation (Momsloven §52) - Delete leftover WeatherForecastController - Fix duplicate migration number 007 (renamed to 007b) - Remove dead code in VatCalculationService (identical if/else branches) Accounting Compliance: - Add missing VAT accounts to StandardDanishAccounts (5610, 5611, 5620) - Populate SAF-T TaxInformation on transaction lines (was always null) - Add AuditFileCountry and TaxRegistrationNumber to SAF-T header Critical Frontend Bugs: - Fix Dashboard <a href> causing full page reloads (now uses React Router Link) - Wire Kassekladde filters to actual data (account, status, date range) - Pre-populate form when editing existing Kassekladde drafts - Add detail drawer for "Vis detaljer" action (was just a toast) - Toggle advanced filters with "Flere filtre" button - CloseFiscalYearWizard now actually posts closing entries via mutations - "Create next year" checkbox now creates the next fiscal year Danish Character Encoding (~50 fixes): - Fix ø/æ/å across Momsindberetning, DocumentUploadModal, Bankafstemning, Kontooversigt, CloseFiscalYearWizard, vatCodes, periodStore, periods, accounting, types/periods Dead Buttons & UX: - Disable Momsindberetning PDF/Export buttons with tooltips - FiscalYearSelector "Administrer" now navigates to Settings - Settings bank tab now uses real BankConnectionsTab component - Bankafstemning save button disabled with development tooltip - Replace hardcoded account options with real API data (Bankafstemning, Fakturaer) - Header help button shows info message, notification bell shows popover Consistency & Quality: - Remove 7 console.log statements from production code - Adopt PageHeader on 6 remaining pages (Kreditnotaer, Settings, Admin, etc.) - Standardize loading states to Skeleton pattern (5 pages) - Replace deprecated bodyStyle prop on Ant Design Cards - Standardize date format to DD-MM-YYYY - Fix sidebar width mismatch in designTokens - Fix Kontooversigt breadcrumb pointing to non-existent route Accessibility: - Add aria-label to sidebar navigation - Add +/- prefix to AmountText for color-blind users - Fix CompanySwitcher permanent skeleton when no companies Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-06 00:18:19 +01:00
// =========================================
// MOMSKONTI (VAT Accounts) - 56xx
// Standard: 5610 = Købsmoms, 5611 = Salgsmoms, 5620 = EU-erhvervelsesmoms
// Required for VAT calculation and reporting
// =========================================
yield return new("5610", "Købsmoms", AccountType.Liability, "Indgående moms (Input VAT)", null, "5610", true);
yield return new("5611", "Salgsmoms", AccountType.Liability, "Udgående moms (Output VAT)", null, "5611", true);
yield return new("5620", "EU-erhvervelsesmoms", AccountType.Liability, "EU acquisition VAT", null, "5620", true);
// =========================================
// PASSIVER - SKYLDIG SKAT OG MOMS (Tax Liabilities) - 79xx
// Standard: 7680 = Anden gæld til SKAT, 7920 = A-skat
// =========================================
yield return new("7900", "Skyldig moms", AccountType.Liability, "Afregning med SKAT", null, "7680", true);
yield return new("7910", "Skyldig A-skat", AccountType.Liability, null, null, "7920", true);
yield return new("7920", "Skyldig AM-bidrag", AccountType.Liability, null, null, "7930", true);
yield return new("7930", "Skyldig ATP", AccountType.Liability, null, null, "7940", true);
yield return new("7940", "Skyldig feriepenge", AccountType.Liability, null, null, "8190", true);
yield return new("7950", "Skyldig selskabsskat", AccountType.Liability, "Selskabsskat til betaling", null, "7670", true);
// =========================================
// AFSKRIVNINGER (Depreciation) - 8xxx
// Standard: 3210 = Af- og nedskrivninger af materielle og immaterielle anlægsaktiver
// =========================================
yield return new("8010", "Afskrivning, bygninger", AccountType.Expense, "Afskrivning på bygninger", null, "3210");
yield return new("8020", "Afskrivning, maskiner", AccountType.Expense, "Afskrivning på maskiner og inventar", null, "3210");
yield return new("8030", "Afskrivning, køretøjer", AccountType.Expense, "Afskrivning på køretøjer", null, "3210");
yield return new("8040", "Småanskaffelser", AccountType.Expense, "Straksafskrivning", "I25", "3210");
// =========================================
// FINANSIELLE POSTER (Financial) - 9xxx
Audit v3: VAT alignment, security, encoding, UX, compliance VAT System Alignment (LEGAL - Critical): - Align frontend VAT codes with backend (S25→U25, K25→I25, etc.) - Add missing codes: UEU, IVV, IVY, REP - Fix output VAT account 5710→5611 to match StandardDanishAccounts - Invoice posting now checks fiscal year status before allowing send - Disallow custom invoice number override (always use auto-numbering) Security: - Fix open redirect in AuthController (validate returnUrl is local) - Store seller CVR/name/address on invoice events (Momsloven §52) Backend Compliance: - Add description validation at posting (Bogføringsloven §7) - SAF-T: add DefaultCurrencyCode, TaxAccountingBasis to header - SAF-T: add TaxTable to MasterFiles with all VAT codes - SAF-T: always write balance elements even when zero - Add financial income account 9100 Renteindtægter Danish Encoding (~25 fixes): - Kassekladde: Bogført, Bogføring, Vælg, være, på, Tilføj, Differens - AttachmentUpload: træk, Understøtter, påkrævet, Bogføringsloven - keyboardShortcuts: Bogfør, Bogføring display name - ShortcutsHelpModal: åbne - DataTable: Genindlæs - documentProcessing: være - CloseFiscalYearWizard: årsafslutning Bugs Fixed: - Non-null assertion crashes in Kunder.tsx and Produkter.tsx (company!.id) - StatusBadge typo "Succces"→"Succes" - HTML entity &oslash; in Kassekladde→proper UTF-8 - AmountText showSign prop was dead code (true || showSign) UX Improvements: - Add PageHeader to Bankafstemning and Dashboard loading/empty states - Responsive columns in Bankafstemning (xs/sm/lg breakpoints) - Disable misleading buttons: Settings preferences, Kontooversigt edit, Loenforstaelse export — with tooltips explaining status - Add DemoDataDisclaimer to UserSettings - Fix breadcrumb self-references on 3 pages - Replace Dashboard fake progress bar with honest message - Standardize date format DD-MM-YYYY in Bankafstemning and Ordrer - Replace Input type="number" with InputNumber in Ordrer Quality: - Remove 8 redundant console.error statements - Fix Kreditnotaer breadcrumb "Salg"→"Fakturering" for consistency Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-06 01:15:45 +01:00
// Standard: 3510 = Finansielle indtægter, 3670 = Øvrige finansielle omkostninger
// =========================================
Audit v3: VAT alignment, security, encoding, UX, compliance VAT System Alignment (LEGAL - Critical): - Align frontend VAT codes with backend (S25→U25, K25→I25, etc.) - Add missing codes: UEU, IVV, IVY, REP - Fix output VAT account 5710→5611 to match StandardDanishAccounts - Invoice posting now checks fiscal year status before allowing send - Disallow custom invoice number override (always use auto-numbering) Security: - Fix open redirect in AuthController (validate returnUrl is local) - Store seller CVR/name/address on invoice events (Momsloven §52) Backend Compliance: - Add description validation at posting (Bogføringsloven §7) - SAF-T: add DefaultCurrencyCode, TaxAccountingBasis to header - SAF-T: add TaxTable to MasterFiles with all VAT codes - SAF-T: always write balance elements even when zero - Add financial income account 9100 Renteindtægter Danish Encoding (~25 fixes): - Kassekladde: Bogført, Bogføring, Vælg, være, på, Tilføj, Differens - AttachmentUpload: træk, Understøtter, påkrævet, Bogføringsloven - keyboardShortcuts: Bogfør, Bogføring display name - ShortcutsHelpModal: åbne - DataTable: Genindlæs - documentProcessing: være - CloseFiscalYearWizard: årsafslutning Bugs Fixed: - Non-null assertion crashes in Kunder.tsx and Produkter.tsx (company!.id) - StatusBadge typo "Succces"→"Succes" - HTML entity &oslash; in Kassekladde→proper UTF-8 - AmountText showSign prop was dead code (true || showSign) UX Improvements: - Add PageHeader to Bankafstemning and Dashboard loading/empty states - Responsive columns in Bankafstemning (xs/sm/lg breakpoints) - Disable misleading buttons: Settings preferences, Kontooversigt edit, Loenforstaelse export — with tooltips explaining status - Add DemoDataDisclaimer to UserSettings - Fix breadcrumb self-references on 3 pages - Replace Dashboard fake progress bar with honest message - Standardize date format DD-MM-YYYY in Bankafstemning and Ordrer - Replace Input type="number" with InputNumber in Ordrer Quality: - Remove 8 redundant console.error statements - Fix Kreditnotaer breadcrumb "Salg"→"Fakturering" for consistency Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-06 01:15:45 +01:00
yield return new("9100", "Renteindtægter", AccountType.Revenue, "Finansielle indtægter", null, "9100");
yield return new("9200", "Bankrenter", AccountType.Financial, null, null, "3670");
yield return new("9210", "Leverandører mv.", AccountType.Financial, "Renter til leverandører", null, "3670");
yield return new("9220", "Ikke-fradragsberettigede renter", AccountType.Financial, null, null, "3670");
}
}