module HAX.Germany.Einkommensteuer where
import HAX.Accounting
import HAX.Bookkeeping
import Control.Monad.Writer
import qualified Data.Map as M
import HAX.Common
import HAX.Germany.Subjekte
import HAX.Germany.Gewerbe
privatVermoegen = "Verm"
ertragsKonten = [ erstattungsUeberhaenge
, privateVerausserungsgeschaefte
, einkuenfteAusBeteiligungenAnKapitalgesellschaftenKapEst
, einkuenfteAusBeteiligungenAnKapitalgesellschaften
, sonstigeEinkuenfteAusKapitalvermoegenKapEst
, sonstigeEinkuenfteAusKapitalvermoegen
, ausserordentlicheEinkuenfte
, arbeitslohn
, steuerfreieEinnahmen
, uebrigeSummeDerEinkuenfte
, einkuenfteAusGewerbebetrieb
]
einkuenfteAusGewerbebetrieb = "EGW"
steuerfreieEinnahmen = "StFrei"
uebrigeSummeDerEinkuenfte = "SdE"
arbeitslohn = "Lohn"
werbungskostensNichtSelbststaendigeArbeitOhneFahrten = "WerbNSA"
ausserordentlicheEinkuenfte = "ausserE"
einkuenfteAusBeteiligungenAnKapitalgesellschaftenKapEst = "EBKK"
einkuenfteAusBeteiligungenAnKapitalgesellschaften = "EBK"
sonstigeEinkuenfteAusKapitalvermoegenKapEst = "EKVE"
sonstigeEinkuenfteAusKapitalvermoegen = "EKV"
privateVerausserungsgeschaefte = "PV"
erstattungsUeberhaenge = "eUeb"
aufwandsKonten = [ sonstigeVorsorgeAufwendungOhneBasisbeitraege
, krankenundPflegeversicherungBasisbeitraege
, privatAusgaben
, altersvorsorgeAufwendungenArbeitgeberAnteil
, altersvorsorgeAufwendungenEigenanteil
, werbungskostensNichtSelbststaendigeArbeitOhneFahrten
, lohnSteuer
]
lohnSteuer = "LSt"
altersvorsorgeAufwendungenEigenanteil = "AvAE"
riesterRentenEinzahlung :: IsString a => a
riesterRentenEinzahlung = error "riesterRente not implemented"
altersvorsorgeAufwendungenArbeitgeberAnteil = "AvAA"
krankenundPflegeversicherungBasisbeitraege = "KPB"
sonstigeVorsorgeAufwendungOhneBasisbeitraege = "sVA"
privatAusgaben = "PA"
type AccNat l w = Acc NatuerlichePerson l w
type Betrag l w = Acc NatuerlichePerson l w Amount
type AccNatRW = AccountingRW NatuerlichePerson
type BetragRW = AccountingRW NatuerlichePerson Amount
type AccNatRO = AccountingReadOnly NatuerlichePerson
type BetragRO = AccountingReadOnly NatuerlichePerson Amount
kinderGeld :: NatuerlichePerson -> Amount
kinderGeld p = g $ kinderMitKindergeld p
where g 1 = 184
g 2 = g 1 + 184
g 3 = g 2 + 190
g n = g (n1) + 215
alter :: Monoid w => AccNat l w Int
alter = do geb <- readBody pGeburt
date <- reader eDate
return $ yearSpan $ dateSpan geb date
jahrInDemManSoAltWird :: Monoid w => Int -> AccNat l w Int
jahrInDemManSoAltWird x = readBody $ getYear . shift (months $ 12 * x) . pGeburt
splitFaktor p = if pSplitting p then 2 else 1
einkommenSteuer :: GewerbeSteuer -> BetragRW
einkommenSteuer gewst = fixed $ pass $ do
name <- reader eName
moeglichkeiten <- forM (range (minBound,maxBound))
$ \v -> (,) v <$> listen (einkommenSteuerVariante gewst v + abgeltungsSteuer v)
let (variante,(steuer,action)) =
minimumBy (comparing $ fst.snd) moeglichkeiten
(soli,actionSoli) <- listen $ solidaritaetsZuschlag gewst variante
let actualAction = do logLedger $ printf "Die Günstigerprüfung ergibt: %v\n" $ show variante
logLedger $ printf "Ersparnisse gegenüber anderen Varianten: %v\n" $
show $ (roundTo 0.(steuer).fst.snd) <$> moeglichkeiten
action
actionSoli
return (steuer, const actualAction)
data Variante = V { mitKinderFreibetrag :: Bool
, teileinkuenfteVerfahren :: Bool
, riesterRente :: ()
, vorsorgeaufwendungenAlteRegel :: ()
}
deriving (Bounded,Eq,Ord,Ix,Show)
type VarBetragRO = Variante -> BetragRO
type VarBetrag l w = Variante -> Betrag l w
einkuefteAusNichtSelbststaendigerArbeit' = do
einkuenfte <- haben arbeitslohn
entfernungsPauschale' <- readBody $ entfernungsPauschale . pWagen
sonstige <- soll werbungskostensNichtSelbststaendigeArbeitOhneFahrten
let werbungskosten = max (entfernungsPauschale' + sonstige)
$ min (positivePart einkuenfte) 1000
singleLog "Enternungspauschale: %v\n" entfernungsPauschale'
singleLog "Werbungskosten (nicht-selbstständige Arbeit): %v\n" werbungskosten
singleLog "Einkuefte Aus nicht-selbstständiger Arbeit: %v\n" einkuenfte
return $ einkuenfte werbungskosten
summeDerEinkuenfte :: VarBetragRO
summeDerEinkuenfte v = singleResult "Summe der Einkuenfte" $
uebrigeSummeDerEinkuenfte' v
+ einkuefteAusNichtSelbststaendigerArbeit'
+ haben ausserordentlicheEinkuenfte
uebrigeSummeDerEinkuenfte' :: VarBetragRO
uebrigeSummeDerEinkuenfte' v = singleResult "Summe der übrigen Einkuenfte" $
haben uebrigeSummeDerEinkuenfte
+ haben sonstigeEinkuenfteAusKapitalvermoegen
+ haben einkuenfteAusBeteiligungenAnKapitalgesellschaften
+ 0.6 * when' (teileinkuenfteVerfahren v)
(haben einkuenfteAusBeteiligungenAnKapitalgesellschaftenKapEst)
einkommenSteuerVariante :: GewerbeSteuer -> VarBetragRO
einkommenSteuerVariante gewst v = do
zvE <- zuVersteuerndesEinkommen v
tariflicheEinkommensteuer <-
return $ steuerBetrag zvE
geminderteTariflicheEinkommensteuer <-
return tariflicheEinkommensteuer
festzusetzendeEinkommensteuer <-
return geminderteTariflicheEinkommensteuer
gewStErmaessigung gewst tariflicheEinkommensteuer
+ kinderGeldAnrechnung v
tell $ logLedger $ printf "festzusetzende Einkommensteuer: %v\n"
festzusetzendeEinkommensteuer
return $ festzusetzendeEinkommensteuer
solidaritaetsZuschlag :: GewerbeSteuer
-> VarBetragRO
solidaritaetsZuschlag gewst v = singleResult "Solidaritätszuschlag" $ withBody $ \p -> do
let soliVariante = v{mitKinderFreibetrag=True}
fEst <- singleResult "Bemessungsgrundlage für Solidaritätszuschlag" $
mute $ einkommenSteuerVariante gewst soliVariante
kinderGeldAnrechnung soliVariante
let zuschlag1 = fEst * solidaritaetsFaktor
zuschlag2 = 0.2 * positivePart (fEst (splitFaktor p * 972))
return $ min zuschlag1 zuschlag2
gewStErmaessigung :: GewerbeSteuer -> Amount
-> BetragRO
gewStErmaessigung gewst est = singleResult "Gewerbesteuerermäßigung" $ do
einkGew <- positivePart $ haben einkuenfteAusGewerbebetrieb
when' (einkGew > 0) $ do
positiveEinkunfte <- sum <$> mapM (positivePart.haben) ertragsKonten
let hoechstBetrag = einkGew / positiveEinkunfte * est
return $ max hoechstBetrag $ max (gwSteuer gewst) $ gwMessbetrag gewst * 3.8
steuerBetrag :: Amount -> Amount
steuerBetrag zvE | zvE <= ks !! 0 = 0
| zvE <= ks !! 1 = (97458/100*y+1400)*y
| zvE <= ks !! 2 = (22874/100*z+2397)*z+971
| zvE <= ks !! 3 = 42/100*(zvEks !! 2)+13971
| True = 45/100*(zvEks !! 3)+97067
where y = (zvEks !! 0)/10000
z = (zvEks !! 1)/10000
ks = [8354,13469,52881,250730];
testSteuerBetrag = all (\(x,y) -> y == steuerBetrag x)
[(4000,0.0)
,(12000,639.9939990728)
,(24000,3748.9578455914)
,(48000,11975.5534967914)
,(120000,42160.98)]
abgeltungsSteuer :: VarBetragRO
abgeltungsSteuer v = withBody $ \p -> do
0.25 * positivePart (haben sonstigeEinkuenfteAusKapitalvermoegenKapEst
+ when' (not $ teileinkuenfteVerfahren v)
(haben einkuenfteAusBeteiligungenAnKapitalgesellschaftenKapEst)
(splitFaktor p * 801))
zuVersteuerndesEinkommen :: VarBetragRO
zuVersteuerndesEinkommen v = do
sdE <- summeDerEinkuenfte v
gesamtbetragderEinkuenfte <-
return sdE
+ assert (>= 0) (haben privateVerausserungsgeschaefte)
"Verlust aus privaten Verausserungsgeschaeften not implemented"
altersentlastungsBetrag v
entlastungsbetragfuerAlleinerziehende
tell $ logLedger $ printf "Gesamtbetrag der Einkünfte: %s" gesamtbetragderEinkuenfte
einkommen <-
return gesamtbetragderEinkuenfte
+ haben erstattungsUeberhaenge
verlustAbzug "ESt" (if sdE < 0 then sdE else positivePart gesamtbetragderEinkuenfte)
sonderAusgaben
aussergewoehnlicheBelastungen
zvE <-
return einkommen
kinderFreibetrag v
tell $ logLedger $ printf "zu versteuerndes Einkommen: %v" zvE
return zvE
mute = censor (const mempty)
aussergewoehnlicheBelastungen = ausbildungsKosten
sonderAusgaben :: BetragRO
sonderAusgaben = allgemeineSonderAusgaben
+ altersvorsorgeAufwendungen
+ sonstigeVorsorgeAufwendung
allgemeineSonderAusgaben :: BetragRO
allgemeineSonderAusgaben = singleResult "allg. Sonderaushaben" $
readBody $ \p -> 36 * splitFaktor p
altersvorsorgeAufwendungen :: BetragRO
altersvorsorgeAufwendungen = singleResult "Altersvorsorgeaufwendungen" $ withBody $ \p -> do
eigenanteil <- soll altersvorsorgeAufwendungenEigenanteil
arbeitgeber <- soll altersvorsorgeAufwendungenArbeitgeberAnteil
jahr <- reader $ fromIntegral . getYear . eDate
let satz = min 1 $ 0.76 + (jahr 2013) * 0.02
return $ satz * (min (splitFaktor p * 20000) $ eigenanteil + arbeitgeber)
arbeitgeber
sonstigeVorsorgeAufwendung :: BetragRO
sonstigeVorsorgeAufwendung = singleResult "sonstige Vorsorgeaufwendungen" $ withBody f
where f p = do
basis <- soll krankenundPflegeversicherungBasisbeitraege
sonstige <- soll sonstigeVorsorgeAufwendungOhneBasisbeitraege
return $ max basis
$ min (hbSelbst + hbPartner)
$ basis + sonstige
where hoechstBetrag ohneZus = if ohneZus then 2800 else 1900
hbSelbst = hoechstBetrag $ krankenUndPflegeOhneZuschuesse p
hbPartner = when' (pSplitting p) $ hoechstBetrag $
fromMaybe (error $ "krankenUndPflegeOhneZuschuessePartner is nothing")
$ krankenUndPflegeOhneZuschuessePartner p
entlastungsbetragfuerAlleinerziehende :: BetragRO
entlastungsbetragfuerAlleinerziehende = withBody $ \p -> do
singleResult "Entlastungsbetrag für Alleinerziehende" $
when' (kinderMitKindergeldImHaushalt p && not (pSplitting p)) 1308
kinderFreibetrag :: VarBetragRO
kinderFreibetrag v = singleResult "Kinderfreibetrag" $
when' (mitKinderFreibetrag v) (fst <$> kinderFreibetragUndGeld)
kinderGeldAnrechnung :: VarBetragRO
kinderGeldAnrechnung v = singleResult "Kindergeld Anrechnung" $
when' (mitKinderFreibetrag v) (snd <$> kinderFreibetragUndGeld)
ausbildungsKosten :: BetragRO
ausbildungsKosten = singleResult "Ausbildungskosten" $ readBody $ \p ->
splitFaktor p * 924/2 * pAuswaertigeKinderInBerufsausbildung p
kinderFreibetragUndGeld :: Monoid w => Acc NatuerlichePerson l w (Amount, Amount)
kinderFreibetragUndGeld = withBody $ \p -> do
let faktor = if pSplitting p || kinderFreibetragsVerdopplung p
then 1 else 0.5
return (faktor * kinderMitKindergeld p * 7008, 12 * faktor * kinderGeld p)
altersentlastungsBetrag :: VarBetragRO
altersentlastungsBetrag v = withBody $ \p -> do
grundlage <- haben arbeitslohn
+ positivePart (mute $ uebrigeSummeDerEinkuenfte' v)
when (pSplitting p) $ error "altersentlastungsBetrag für Splitting not implemented"
(prozent, hoechst) <- eigenerAltersentlastungsSatz <$> jahrInDemManSoAltWird 65
singleResult "Altersentlastungsbetrag" $
return $ min hoechst $ grundlage * prozent
where eigenerAltersentlastungsSatz jahr = if jahr > 2014 then (0,0)
else altersentlastungsSaetze ! jahr
altersentlastungsSaetze :: Array Int (Amount,Amount)
altersentlastungsSaetze = assocArray [( 2005,( 40.0, 1900 ))
,( 2006,( 38.4, 1824 ))
,( 2007,( 36.8, 1748 ))
,( 2008,( 35.2, 1672 ))
,( 2009,( 33.6, 1596 ))
,( 2010,( 32.0, 1520 ))
,( 2011,( 30.4, 1444 ))
,( 2012,( 28.8, 1368 ))
,( 2013,( 27.2, 1292 ))
,( 2014,( 25.6, 1216 ))
,( 2015,( 24.0, 1140 ))
,( 2016,( 22.4, 1064 ))
,( 2017,( 20.8, 988 ))
,( 2018,( 19.2, 912 ))
,( 2019,( 17.6, 836 ))
,( 2020,( 16.0, 760 ))
,( 2021,( 15.2, 722 ))
,( 2022,( 14.4, 684 ))
,( 2023,( 13.6, 646 ))
,( 2024,( 12.8, 608 ))
,( 2025,( 12.0, 570 ))
,( 2026,( 11.2, 532 ))
,( 2027,( 10.4, 494 ))
,( 2028,( 9.6, 456 ))
,( 2029,( 8.8, 418 ))
,( 2030,( 8.0, 380 ))
,( 2031,( 7.2, 342 ))
,( 2032,( 6.4, 304 ))
,( 2033,( 5.6, 266 ))
,( 2034,( 4.8, 228 ))
,( 2035,( 4.0, 190 ))
,( 2036,( 3.2, 152 ))
,( 2037,( 2.4, 114 ))
,( 2038,( 1.6, 76 ))
,( 2039,( 0.8, 38 ))
,( 2040,( 0.0, 0 ))
]