books/backend/Books.Api/Domain/VatCode.cs
Nicolaj Hartmann 8096a19081 Audit v4: VAT calc, SAF-T compliance, security hardening, frontend quality
Backend (17 files):
- VAT: REP 25% deductibility (§42), EU reverse charge double-entry (IEUV/IEUY/IVY),
  IVY rate 0%→25%, VatReport Box C/D populated, Basis1 from real revenue
- SAF-T: correct OECD namespace, closing balance net calc, zero-amount fallback,
  credit note auto-numbering (§52)
- Security: BankingController CSRF state token + company auth check,
  attachment canonical path traversal check, discount 0-100% validation,
  deactivated product/customer update guard
- Quality: redact bank API logs, remove dead code (VatCalcService,
  PaymentMatchingService), CompanyAggregate IEmit interfaces, fix URL encoding

Frontend (15 files):
- Fix double "kr." in AmountText and Dashboard Statistic components
- Fix UserSettings Switch defaultChecked desync with Form state
- Remove dual useCompany/useCompanyStore pattern (Dashboard, Moms, Bank)
- Correct SKAT VAT deadline calculation per period type
- Add half-yearly/yearly VAT period options
- Guard console.error with import.meta.env.DEV
- Use shared formatDate in BankConnectionsTab
- Remove dead NONE vatCode check, purge 7 legacy VAT codes from type union
- Migrate S25→U25, K25→I25 across all pages

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-06 01:38:52 +01:00

90 lines
3.6 KiB
C#

namespace Books.Api.Domain;
/// <summary>
/// Danish VAT (moms) codes for SKAT compliance.
/// These codes determine how transactions are reported in the VAT return.
/// </summary>
public static class VatCodes
{
// Output VAT (Udgående moms) - Sales
public const string U25 = "U25"; // Salg med 25% moms (standard)
public const string UEU = "UEU"; // EU-salg af varer/ydelser (momsfrit)
public const string UEXP = "UEXP"; // Eksport uden for EU (momsfrit)
// Input VAT (Indgående moms) - Purchases
public const string I25 = "I25"; // Køb med 25% moms (fuldt fradrag)
public const string IEUV = "IEUV"; // EU-erhvervelse varer (reverse charge)
public const string IEUY = "IEUY"; // EU-erhvervelse ydelser (reverse charge)
public const string IVV = "IVV"; // Import varer fra verden
public const string IVY = "IVY"; // Import ydelser fra verden
// Special codes
public const string REP = "REP"; // Repræsentation (25% fradrag)
public const string INGEN = "INGEN"; // Ingen moms / momsfrit
/// <summary>
/// Gets the VAT rate for a given code.
/// </summary>
public static decimal GetRate(string? vatCode) => vatCode switch
{
U25 => 0.25m,
I25 => 0.25m,
IEUV => 0.25m, // Reverse charge - calculated but offset
IEUY => 0.25m, // Reverse charge - calculated but offset
IVY => 0.25m, // Reverse charge for imported services (Momsloven §46)
REP => 0.25m, // 25% rate, but only 25% deductible
_ => 0m
};
/// <summary>
/// Gets the deductible percentage for a given code.
/// </summary>
public static decimal GetDeductiblePercent(string? vatCode) => vatCode switch
{
I25 => 1.0m, // 100% deductible
IEUV => 1.0m, // 100% deductible (reverse charge)
IEUY => 1.0m, // 100% deductible (reverse charge)
IVY => 1.0m, // 100% deductible (reverse charge, Momsloven §46)
REP => 0.25m, // Only 25% deductible
_ => 0m
};
/// <summary>
/// Validates if a VAT code is valid.
/// </summary>
public static bool IsValid(string? vatCode)
{
if (string.IsNullOrWhiteSpace(vatCode)) return true; // null/empty is allowed
return vatCode is U25 or UEU or UEXP or I25 or IEUV or IEUY or IVV or IVY or REP or INGEN;
}
/// <summary>
/// Determines if this is input VAT (købsmoms/fradrag) vs output VAT (salgsmoms/skyldig).
/// Input VAT is on purchases (assets), output VAT is on sales (liabilities).
/// </summary>
public static bool IsInputVat(string? vatCode) => vatCode switch
{
I25 or IEUV or IEUY or IVV or IVY or REP => true, // Purchase/input VAT codes
U25 or UEU or UEXP => false, // Sales/output VAT codes
_ => false
};
/// <summary>
/// All valid VAT codes for UI dropdowns.
/// </summary>
public static IReadOnlyList<VatCodeInfo> All { get; } =
[
new(INGEN, "Ingen moms", "No VAT", 0m),
new(U25, "Salg 25%", "Sales 25%", 0.25m),
new(UEU, "EU-salg", "EU Sales", 0m),
new(UEXP, "Eksport", "Export", 0m),
new(I25, "Køb 25%", "Purchase 25%", 0.25m),
new(IEUV, "EU-køb varer", "EU Purchase Goods", 0.25m),
new(IEUY, "EU-køb ydelser", "EU Purchase Services", 0.25m),
new(IVV, "Import varer", "Import Goods", 0m),
new(IVY, "Import ydelser", "Import Services", 0.25m),
new(REP, "Repræsentation", "Entertainment", 0.25m),
];
}
public record VatCodeInfo(string Code, string NameDanish, string NameEnglish, decimal Rate);