AccountingService.java
package com.tdmconsult.ete.accounting;
import static com.tdmconsult.ete.accounting.SKR03Account.A1120;
import static com.tdmconsult.ete.accounting.SKR03Account.A1348;
import static com.tdmconsult.ete.accounting.SKR03Account.A2213;
import static com.tdmconsult.ete.accounting.SKR03Account.A2216;
import static com.tdmconsult.ete.accounting.SKR03Account.A2219;
import static com.tdmconsult.ete.accounting.SKR03Account.A2325;
import static com.tdmconsult.ete.accounting.SKR03Account.A2654;
import static com.tdmconsult.ete.accounting.SKR03Account.A2725;
import com.tdmconsult.ete.evaluation.EvaluationHrCalculator;
import com.tdmconsult.ete.evaluation.EvaluationService;
import com.tdmconsult.ete.evaluation.EvaluationState;
import com.tdmconsult.ete.masterdata.Security;
import com.tdmconsult.ete.transactions.PaymentTransactionRepository;
import java.math.BigDecimal;
import java.time.LocalDate;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.stream.Stream;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@Service
@Transactional
@RequiredArgsConstructor
public class AccountingService {
public static final List<AccountingRecord> EB = List.of(
AccountingRecord.builder()
.setTradingDate(LocalDate.of(2025, 1, 1))
.setDebitAccount(SKR03Account.A1348)
.setCreditAccount(SKR03Account.A9000)
.setAmount(new BigDecimal("1883553.68"))
.setDescription("EB Wert").build()
);
private final EvaluationService evaluationService;
private final PaymentTransactionRepository paymentTransactionRepository;
private List<AccountingRecord> createAccountingRecordsForPayments(final int fiscalYear) {
final var records = new ArrayList<AccountingRecord>();
final var payments = paymentTransactionRepository.findAll().stream()
.filter(x -> x.getTradingDate().getYear() == fiscalYear)
.toList();
payments.forEach(p -> {
records.add(AccountingRecord.builder()
.setDebitAccount(A1120)
.setCreditAccount(A2654)
.setSource(p)
.setTradingDate(p.getTradingDate())
.setAmount(p.getNetAmount())
.setDescription("%s %s %d ST".formatted(
p.getType().getDisplayName(),
p.getSecurity().getName(),
p.getQuantity())).build());
if (p.getDomesticWithholdingTax() != null) {
records.add(AccountingRecord.builder()
.setDebitAccount(A2213)
.setCreditAccount(A2654)
.setSource(p)
.setTradingDate(p.getTradingDate())
.setAmount(p.getDomesticWithholdingTax())
.setDescription("%s %s %d ST KapSt".formatted(
p.getType().getDisplayName(),
p.getSecurity().getName(),
p.getQuantity())).build());
}
if (p.getDomesticWithholdingSoli() != null) {
records.add(AccountingRecord.builder()
.setDebitAccount(A2216)
.setCreditAccount(A2654)
.setSource(p)
.setTradingDate(p.getTradingDate())
.setAmount(p.getDomesticWithholdingSoli())
.setDescription("%s %s %d ST SolZ auf KapSt".formatted(
p.getType().getDisplayName(),
p.getSecurity().getName(),
p.getQuantity())).build());
}
if (p.getForeignWithholdingTax() != null) {
records.add(AccountingRecord.builder()
.setDebitAccount(A2219)
.setCreditAccount(A2654)
.setSource(p)
.setTradingDate(p.getTradingDate())
.setAmount(p.getForeignWithholdingTax())
.setDescription("%s %s %d ST Quellensteuer".formatted(
p.getType().getDisplayName(),
p.getSecurity().getName(),
p.getQuantity())).build());
}
});
return records;
}
private List<AccountingRecord> getSecurityAccountRecords(final List<EvaluationState> evaluations) {
final var records = new ArrayList<AccountingRecord>();
evaluations.stream().forEach(evaluation -> {
evaluation.getPositions().forEach(position -> {
if (position.isBuy()) {
records.add(AccountingRecord.builder()
.setSource(position.getSource().getPortfolioTransaction())
.setDescription("WP-Kauf %s %s ST".formatted(
position.getSource().getSecurity().getName(),
position.getQuantity()))
.setTradingDate(position.getTradingDate())
.setAmount(position.getTotalCost())
.setDebitAccount(A1348)
.setCreditAccount(A1120).build());
} else if (position.isSell()) {
records.add(AccountingRecord.builder()
.setSource(position.getSource().getPortfolioTransaction())
.setDescription("WP-%s %s %s ST".formatted(
position.getSource().getSecurity().getType() == Security.EquityType.BOND
? "Rückzahlung"
: "Verkauf",
position.getSource().getSecurity().getName(),
position.getQuantity()))
.setTradingDate(position.getTradingDate())
.setAmount(position.getTotalCost())
.setDebitAccount(A1120)
.setCreditAccount(A1348).build());
if (position.getProfitLoss().signum() != 0) {
records.add(AccountingRecord.builder()
.setSource(position.getSource().getPortfolioTransaction())
.setDescription("WP-Verkauf %s %s %s ST".formatted(
position.getProfitLoss().signum() > 0 ? "Gewinn" : "Verlust",
position.getSource().getSecurity().getName(),
position.getSource().getQuantity()))
.setTradingDate(position.getTradingDate())
.setAmount(position.getProfitLoss().abs())
.setDebitAccount(position.getProfitLoss().signum() > 0 ? A1348 : A2325)
.setCreditAccount(position.getProfitLoss().signum() > 0 ? A2725 : A1348).build());
}
}
});
final var hrResult = new EvaluationHrCalculator(evaluation).calculate();
switch (hrResult.action()) {
case VALUE_INCREASE_WITH_WRITE_UP -> records.add(AccountingRecord.builder()
.setSource(null)
.setDebitAccount(SKR03Account.A1348)
.setCreditAccount(SKR03Account.A2715)
.setAmount(hrResult.amount())
.setDescription("JA Zuschreibung %s".formatted(evaluation.getSecurity().getName()))
.setTradingDate(LocalDate.of(evaluation.getFiscalYear(), 12, 31)).build());
case VALUE_DECREASE_WITH_WRITE_DOWN -> records.add(AccountingRecord.builder()
.setSource(null)
.setDebitAccount(SKR03Account.A4875)
.setCreditAccount(SKR03Account.A1348)
.setAmount(hrResult.amount())
.setDescription("JA Abschreibung %s".formatted(evaluation.getSecurity().getName()))
.setTradingDate(LocalDate.of(evaluation.getFiscalYear(), 12, 31)).build());
default -> {}
}
});
return records;
}
public List<AccountingRecord> getAccountingRecords(final int fiscalYear, final int accountNumber) {
final var evaluations = evaluationService.evaluate(fiscalYear);
final var securityRecords = getSecurityAccountRecords(evaluations);
final var ebRecords = EB.stream()
.map(account -> AccountingRecord.builder()
.setDebitAccount(account.getDebitAccount())
.setCreditAccount(account.getCreditAccount())
.setAmount(account.getAmount())
.setDescription("EB-Wert")
.setTradingDate(account.getTradingDate()).build()).toList();
final var paymentsRecords = createAccountingRecordsForPayments(fiscalYear);
return Stream.of(securityRecords, ebRecords, paymentsRecords)
.flatMap(Collection::stream)
.filter(r -> r.getTradingDate().getYear() == fiscalYear)
.filter(r ->
r.getCreditAccount().getAccountNumber() == accountNumber
|| r.getDebitAccount().getAccountNumber() == accountNumber)
.toList();
}
}