package com.goldenchart.service; import com.goldenchart.dto.PaperLedgerEntryDto; import com.goldenchart.dto.PaperPageDto; import com.goldenchart.entity.GcPaperAccount; import com.goldenchart.entity.GcPaperCashLedger; import com.goldenchart.entity.GcPaperTrade; import com.goldenchart.repository.GcPaperCashLedgerRepository; import lombok.RequiredArgsConstructor; import org.springframework.data.domain.Page; import org.springframework.data.domain.PageRequest; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import java.math.BigDecimal; import java.time.LocalDateTime; @Service @RequiredArgsConstructor @Transactional public class PaperLedgerService { public static final String INITIAL = "INITIAL"; public static final String BUY_SETTLE = "BUY_SETTLE"; public static final String SELL_SETTLE = "SELL_SETTLE"; public static final String RESET = "RESET"; private final GcPaperCashLedgerRepository ledgerRepo; public void recordInitial(GcPaperAccount account, BigDecimal initialCash) { ledgerRepo.save(GcPaperCashLedger.builder() .accountId(account.getId()) .entryType(INITIAL) .amount(initialCash) .cashBefore(BigDecimal.ZERO) .cashAfter(initialCash) .build()); } public void recordBuySettle(GcPaperAccount account, GcPaperTrade trade, BigDecimal totalCost) { BigDecimal before = account.getCashBalance().add(totalCost); ledgerRepo.save(GcPaperCashLedger.builder() .accountId(account.getId()) .entryType(BUY_SETTLE) .amount(totalCost.negate()) .cashBefore(before) .cashAfter(account.getCashBalance()) .refTradeId(trade.getId()) .symbol(trade.getSymbol()) .build()); } public void recordSellSettle(GcPaperAccount account, GcPaperTrade trade, BigDecimal netProceeds) { BigDecimal before = account.getCashBalance().subtract(netProceeds); ledgerRepo.save(GcPaperCashLedger.builder() .accountId(account.getId()) .entryType(SELL_SETTLE) .amount(netProceeds) .cashBefore(before) .cashAfter(account.getCashBalance()) .refTradeId(trade.getId()) .symbol(trade.getSymbol()) .build()); } public void recordReset(GcPaperAccount account, BigDecimal cashBefore, BigDecimal newCash) { ledgerRepo.save(GcPaperCashLedger.builder() .accountId(account.getId()) .entryType(RESET) .amount(newCash.subtract(cashBefore)) .cashBefore(cashBefore) .cashAfter(newCash) .build()); } @Transactional(readOnly = true) public PaperPageDto list(Long accountId, LocalDateTime from, LocalDateTime to, int page, int size) { PageRequest pr = PageRequest.of(Math.max(0, page), Math.min(100, Math.max(1, size))); Page result = (from != null || to != null) ? ledgerRepo.findByAccountIdAndCreatedAtBetweenOrderByCreatedAtDesc( accountId, from != null ? from : LocalDateTime.of(2000, 1, 1, 0, 0), to != null ? to : LocalDateTime.now().plusYears(10), pr) : ledgerRepo.findByAccountIdOrderByCreatedAtDesc(accountId, pr); return PaperPageDto.builder() .content(result.getContent().stream().map(this::toDto).toList()) .page(result.getNumber()) .size(result.getSize()) .totalElements(result.getTotalElements()) .totalPages(result.getTotalPages()) .build(); } private PaperLedgerEntryDto toDto(GcPaperCashLedger e) { return PaperLedgerEntryDto.builder() .id(e.getId()) .entryType(e.getEntryType()) .amount(e.getAmount().doubleValue()) .cashBefore(e.getCashBefore().doubleValue()) .cashAfter(e.getCashAfter().doubleValue()) .refTradeId(e.getRefTradeId()) .symbol(e.getSymbol()) .createdAt(e.getCreatedAt() != null ? e.getCreatedAt().toString() : null) .build(); } }