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>
90 lines
3.6 KiB
C#
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);
|