Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fixed tax withholding calculation in the Targo Bank PDF importer #4547

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -1748,7 +1748,7 @@ public void testSteuerbehandlungVonDividende07()
hasDate("2024-12-18T00:00"), hasShares(4.00), //
hasSource("SteuerbehandlungVonDividende07.txt"), //
hasNote("Tr.-Nr.: INDTBK35424CG007898O00"), //
hasAmount("EUR", 0.83), hasGrossValue("EUR", 0.83), //
hasAmount("EUR", 0.83 + 1.18), hasGrossValue("EUR", 0.83 + 1.18), //
hasTaxes("EUR", 0.00), hasFees("EUR", 0.00))));
}

Expand Down Expand Up @@ -1781,9 +1781,9 @@ public void testDividende07MitSteuerbehandlungVonDividende07()
hasDate("2024-12-18T00:00"), hasShares(4.00), //
hasSource("Dividende07.txt; SteuerbehandlungVonDividende07.txt"), //
hasNote("R.-Nr.: CPS-2024-0223620171-0003969 | Tr.-Nr.: INDTBK35424CG007898O00"), //
hasAmount("EUR", 7.06), hasGrossValue("EUR", 7.89), //
hasAmount("EUR", 7.06 - 1.18), hasGrossValue("EUR", 7.89), //
hasForexGrossValue("USD", 8.24), //
hasTaxes("EUR", 0.83), hasFees("EUR", 0.00))));
hasTaxes("EUR", 0.83 + 1.18), hasFees("EUR", 0.00))));
}

@Test
Expand Down Expand Up @@ -1816,8 +1816,8 @@ public void testDividende07MitSteuerbehandlungVonDividende03WithSecurityInEUR()
hasDate("2024-12-18T00:00"), hasShares(4.00), //
hasSource("Dividende07.txt; SteuerbehandlungVonDividende07.txt"), //
hasNote("R.-Nr.: CPS-2024-0223620171-0003969 | Tr.-Nr.: INDTBK35424CG007898O00"), //
hasAmount("EUR", 7.06), hasGrossValue("EUR", 7.89), //
hasTaxes("EUR", 0.83), hasFees("EUR", 0.00), //
hasAmount("EUR", 7.06 - 1.18), hasGrossValue("EUR", 7.89), //
hasTaxes("EUR", 0.83 + 1.18), hasFees("EUR", 0.00), //
check(tx -> {
CheckCurrenciesAction c = new CheckCurrenciesAction();
Account account = new Account();
Expand Down Expand Up @@ -1856,8 +1856,8 @@ public void testDividende07MitSteuerbehandlungVonDividende07_SourceFilesReversed
hasDate("2024-12-18T00:00"), hasShares(4.00), //
hasSource("Dividende07.txt; SteuerbehandlungVonDividende07.txt"), //
hasNote("R.-Nr.: CPS-2024-0223620171-0003969 | Tr.-Nr.: INDTBK35424CG007898O00"), //
hasAmount("EUR", 7.06), hasGrossValue("EUR", 7.89), //
hasTaxes("EUR", 0.83), hasFees("EUR", 0.00))));
hasAmount("EUR", 7.06 - 1.18), hasGrossValue("EUR", 7.89), //
hasTaxes("EUR", 0.83 + 1.18), hasFees("EUR", 0.00))));
}

@Test
Expand Down Expand Up @@ -1890,8 +1890,8 @@ public void testDividende07MitSteuerbehandlungVonDividende07WithSecurityInEUR_So
hasDate("2024-12-18T00:00"), hasShares(4.00), //
hasSource("Dividende07.txt; SteuerbehandlungVonDividende07.txt"), //
hasNote("R.-Nr.: CPS-2024-0223620171-0003969 | Tr.-Nr.: INDTBK35424CG007898O00"), //
hasAmount("EUR", 7.06), hasGrossValue("EUR", 7.89), //
hasTaxes("EUR", 0.83), hasFees("EUR", 0.00), //
hasAmount("EUR", 7.06 - 1.18), hasGrossValue("EUR", 7.89), //
hasTaxes("EUR", 0.83 + 1.18), hasFees("EUR", 0.00), //
check(tx -> {
CheckCurrenciesAction c = new CheckCurrenciesAction();
Account account = new Account();
Expand Down Expand Up @@ -2000,7 +2000,7 @@ public void testSteuerbehandlungVonDividende08()
hasDate("2020-10-01T00:00"), hasShares(127.00), //
hasSource("SteuerbehandlungVonDividende08.txt"), //
hasNote("Tr.-Nr.: INDTBK27620CG00000"), //
hasAmount("EUR", 4.68), hasGrossValue("EUR", 4.68), //
hasAmount("EUR", 4.68 + 6.65), hasGrossValue("EUR", 4.68 + 6.65), //
hasTaxes("EUR", 0.00), hasFees("EUR", 0.00))));
}

Expand Down Expand Up @@ -2033,9 +2033,9 @@ public void testDividende08MitSteuerbehandlungVonDividende08()
hasDate("2020-10-01T00:00"), hasShares(127.00), //
hasSource("Dividende08.txt; SteuerbehandlungVonDividende08.txt"), //
hasNote("R.-Nr.: CPS-2020-0223111111-0001111 | Tr.-Nr.: INDTBK27620CG00000"), //
hasAmount("EUR", 39.67), hasGrossValue("EUR", 44.35), //
hasAmount("EUR", 33.02), hasGrossValue("EUR", 44.35), //
hasForexGrossValue("USD", 52.07), //
hasTaxes("EUR", 4.68), hasFees("EUR", 0.00))));
hasTaxes("EUR", 4.68 + 6.65), hasFees("EUR", 0.00))));
}

@Test
Expand Down Expand Up @@ -2068,8 +2068,8 @@ public void testDividende08MitSteuerbehandlungVonDividende08WithSecurityInEUR()
hasDate("2020-10-01T00:00"), hasShares(127.00), //
hasSource("Dividende08.txt; SteuerbehandlungVonDividende08.txt"), //
hasNote("R.-Nr.: CPS-2020-0223111111-0001111 | Tr.-Nr.: INDTBK27620CG00000"), //
hasAmount("EUR", 39.67), hasGrossValue("EUR", 44.35), //
hasTaxes("EUR", 4.68), hasFees("EUR", 0.00), //
hasAmount("EUR", 39.67 - 6.65), hasGrossValue("EUR", 44.35), //
hasTaxes("EUR", 4.68 + 6.65), hasFees("EUR", 0.00), //
check(tx -> {
CheckCurrenciesAction c = new CheckCurrenciesAction();
Account account = new Account();
Expand All @@ -2080,7 +2080,7 @@ public void testDividende08MitSteuerbehandlungVonDividende08WithSecurityInEUR()
}

@Test
public void testDividende08MitSteuerbehandlungVonDividende07_SourceFilesReversed()
public void testDividende08MitSteuerbehandlungVonDividende08_SourceFilesReversed()
{
TargobankPDFExtractor extractor = new TargobankPDFExtractor(new Client());

Expand Down Expand Up @@ -2108,8 +2108,8 @@ public void testDividende08MitSteuerbehandlungVonDividende07_SourceFilesReversed
hasDate("2020-10-01T00:00"), hasShares(127.00), //
hasSource("Dividende08.txt; SteuerbehandlungVonDividende08.txt"), //
hasNote("R.-Nr.: CPS-2020-0223111111-0001111 | Tr.-Nr.: INDTBK27620CG00000"), //
hasAmount("EUR", 39.67), hasGrossValue("EUR", 44.35), //
hasTaxes("EUR", 4.68), hasFees("EUR", 0.00))));
hasAmount("EUR", 39.67 - 6.65), hasGrossValue("EUR", 44.35), //
hasTaxes("EUR", 4.68 + 6.65), hasFees("EUR", 0.00))));
}

@Test
Expand Down Expand Up @@ -2142,8 +2142,8 @@ public void testDividende08MitSteuerbehandlungVonDividende08WithSecurityInEUR_So
hasDate("2020-10-01T00:00"), hasShares(127.00), //
hasSource("Dividende08.txt; SteuerbehandlungVonDividende08.txt"), //
hasNote("R.-Nr.: CPS-2020-0223111111-0001111 | Tr.-Nr.: INDTBK27620CG00000"), //
hasAmount("EUR", 39.67), hasGrossValue("EUR", 44.35), //
hasTaxes("EUR", 4.68), hasFees("EUR", 0.00), //
hasAmount("EUR", 39.67 - 6.65), hasGrossValue("EUR", 44.35), //
hasTaxes("EUR", 4.68 + 6.65), hasFees("EUR", 0.00), //
check(tx -> {
CheckCurrenciesAction c = new CheckCurrenciesAction();
Account account = new Account();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -350,7 +350,10 @@ private void addTaxesTreatmentTransaction()
// Gesamtsumme Steuern 5,59 EUR
// @formatter:on
section -> section //
.attributes("partialExemptionTaxes", "currencypartialExemptionTaxes", "grossBeforeTaxes", "currencyBeforeTaxes", "grossAssessmentBasis", "currencyAssessmentBasis", "deductedTaxes", "currencyDeductedTaxes") //
.attributes("partialExemptionTaxes", "currencypartialExemptionTaxes", //
"grossBeforeTaxes", "currencyBeforeTaxes", //
"grossAssessmentBasis", "currencyAssessmentBasis", //
"deductedTaxes", "currencyDeductedTaxes") //
.match("^Teilfreistellung \\(§ 20 InvStG\\) [\\.,\\d]+ % \\- (?<partialExemptionTaxes>[\\.,\\d]+) (?<currencypartialExemptionTaxes>[\\w]{3})$") //
.match("^Ertr.ge\\/Verluste (?<grossBeforeTaxes>[\\.,\\d]+) (?<currencyBeforeTaxes>[\\w]{3})$") //
.match("^Bemessungsgrundlage (?<grossAssessmentBasis>[\\.,\\d]+) (?<currencyAssessmentBasis>[\\w]{3})$") //
Expand Down Expand Up @@ -380,12 +383,60 @@ private void addTaxesTreatmentTransaction()
}
}),
// @formatter:off
// 1.) No tax burden: The losses offset the income so that no withholding tax is payable.
// 2.) Offsetting only with tax liability: Withholding tax can only be offset if capital gains tax is levied in Germany.
// 3.) Loss offsetting: The offsetting reduces the tax assessment basis to zero, which also eliminates offsetting.
// 4.) Savers' lump sum/exemption order: If used, this could also reduce the tax burden to zero.
//
// See test case of taxes treatment transaction ...Dividende05 vs. ...Dividende08
// ---------------------------------------------------
// Anrechenbare ausländische Quellensteuer 3,67 EUR
// Erträge/Verluste 24,49 EUR
// Anrechnung ausländischer Quellensteuer ** 0,00 EUR
// Bemessungsgrundlage 0,00 EUR
// Gesamtsumme Steuern 0,00 EUR
//
// Anrechenbare ausländische Quellensteuer 6,65 EUR
// Erträge/Verluste 44,35 EUR
// Anrechnung ausländischer Quellensteuer ** - 26,60 EUR
// Bemessungsgrundlage 17,75 EUR
// Gesamtsumme Steuern 4,68 EUR
// @formatter:on
section -> section //
.attributes("withHoldingTaxes", "currencyWithHoldingTaxes", //
"grossBeforeTaxes", "currencyBeforeTaxes", //
"withholdingTaxesTimes4", "currencyWithholdingTaxesTimes4", //
"grossAssessmentBasis", "currencyAssessmentBasis", //
"deductedTaxes", "currencyDeductedTaxes") //
.match("^Anrechenbare ausl.ndische Quellensteuer (?<withHoldingTaxes>[\\.,\\d]+) (?<currencyWithHoldingTaxes>[\\w]{3})$") //
.match("^Ertr.ge\\/Verluste (?<grossBeforeTaxes>[\\.,\\d]+) (?<currencyBeforeTaxes>[\\w]{3})$") //
.match("^Anrechnung ausl.ndischer Quellensteuer.* (?<withholdingTaxesTimes4>[\\.,\\d]+) (?<currencyWithholdingTaxesTimes4>[\\w]{3})$") //
.match("^Bemessungsgrundlage (?<grossAssessmentBasis>[\\.,\\d]+) (?<currencyAssessmentBasis>[\\w]{3})$") //
.match("^Gesamtsumme Steuern (?<deductedTaxes>[\\.,\\d]+) (?<currencyDeductedTaxes>[\\w]{3})$") //
.assign((t, v) -> {
Money withHoldingTaxes = Money.of(asCurrencyCode(v.get("currencyWithHoldingTaxes")), asAmount(v.get("withHoldingTaxes")));
Money grossBeforeTaxes = Money.of(asCurrencyCode(v.get("currencyBeforeTaxes")), asAmount(v.get("grossBeforeTaxes")));
Money withholdingTaxesTimes4 = Money.of(asCurrencyCode(v.get("currencyWithholdingTaxesTimes4")), asAmount(v.get("withholdingTaxesTimes4")));
Money grossAssessmentBasis = Money.of(asCurrencyCode(v.get("currencyAssessmentBasis")), asAmount(v.get("grossAssessmentBasis")));
Money deductedTaxes = Money.of(asCurrencyCode(v.get("currencyDeductedTaxes")), asAmount(v.get("deductedTaxes")));

// Store in transaction context
v.getTransactionContext().put(ATTRIBUTE_GROSS_TAXES_TREATMENT, grossBeforeTaxes);

if (!grossAssessmentBasis.isZero() && withholdingTaxesTimes4.divide(4).equals(withHoldingTaxes))
t.setMonetaryAmount(deductedTaxes.add(withHoldingTaxes));
else
t.setMonetaryAmount(deductedTaxes);
}),
// @formatter:off
// Erträge/Verluste 3.123,25 EUR
// Bemessungsgrundlage 3.123,25 EUR
// Gesamtsumme Steuern 823,76 EUR
// @formatter:on
section -> section //
.attributes("grossBeforeTaxes", "currencyBeforeTaxes", "grossAssessmentBasis", "currencyAssessmentBasis", "deductedTaxes", "currencyDeductedTaxes") //
.attributes("grossBeforeTaxes", "currencyBeforeTaxes", //
"grossAssessmentBasis", "currencyAssessmentBasis", //
"deductedTaxes", "currencyDeductedTaxes") //
.match("^Ertr.ge\\/Verluste (?<grossBeforeTaxes>[\\.,\\d]+) (?<currencyBeforeTaxes>[\\w]{3})$") //
.match("^Bemessungsgrundlage (?<grossAssessmentBasis>[\\.,\\d]+) (?<currencyAssessmentBasis>[\\w]{3})$") //
.match("^Gesamtsumme Steuern (?<deductedTaxes>[\\.,\\d]+) (?<currencyDeductedTaxes>[\\w]{3})$") //
Expand All @@ -397,10 +448,10 @@ private void addTaxesTreatmentTransaction()
// Calculate the taxes and store gross amount
if (!grossBeforeTaxes.isZero() && grossAssessmentBasis.isGreaterThan(grossBeforeTaxes))
{
t.setMonetaryAmount(grossAssessmentBasis.subtract(grossBeforeTaxes).add(deductedTaxes));

// Store in transaction context
v.getTransactionContext().put(ATTRIBUTE_GROSS_TAXES_TREATMENT, grossAssessmentBasis);

t.setMonetaryAmount(grossAssessmentBasis.subtract(grossBeforeTaxes).add(deductedTaxes));
}
else
{
Expand Down Expand Up @@ -430,6 +481,7 @@ private void addTaxesTreatmentTransaction()
{
// Store in transaction context
v.getTransactionContext().put(ATTRIBUTE_GROSS_TAXES_TREATMENT, grossBeforeTaxes);

t.setMonetaryAmount(grossBeforeTaxes);
}
}))
Expand Down