Files
Distribution_GL/Repositories/JournalEntryRepository.cs
T

117 lines
5.0 KiB
C#

using General_Ledger_Service.DTOs.Reports;
using General_Ledger_Service.Infra;
using General_Ledger_Service.Interfaces.Repositories;
using General_Ledger_Service.Models;
using Microsoft.EntityFrameworkCore;
namespace General_Ledger_Service.Repositories
{
public class JournalEntryRepository(AppDBContext context) : IJournalEntryRepository
{
private readonly AppDBContext _context = context;
public async Task<JournalEntry?> GetJournalEntryByJournalNoAsync(string journalNo) =>
await _context.JournalEntries
.Include(j => j.Lines)
.FirstOrDefaultAsync(j => j.JournalNo == journalNo);
public async Task<JournalEntry?> GetJournalEntryBySourceReferenceAsync(string sourceModule, string reference) =>
await _context.JournalEntries
.Include(j => j.Lines)
.FirstOrDefaultAsync(j => j.SourceModule == sourceModule && j.Reference == reference);
public async Task<int> CountJournalEntriesForYearAsync(int year) =>
await _context.JournalEntries.CountAsync(j => j.JournalNo.StartsWith($"JV-{year}-"));
public Task<JournalEntry> AddJournalEntryAsync(JournalEntry entry) =>
RepositoryHelpers.AddAsync(_context.JournalEntries, entry);
public async Task AddJournalEntryLinesAsync(IEnumerable<JournalEntryLine> lines) =>
await _context.JournalEntryLines.AddRangeAsync(lines);
public async Task<List<GeneralLedgerLineDto>> GetGeneralLedgerLinesAsync(long accountId, DateOnly periodStart, DateOnly periodEnd)
{
var treeRows = await _context.Accounts.AsNoTracking()
.Select(a => new RecursiveTreeQueries.TreeRow
{
Id = a.AccountId,
Code = a.AccountCode,
Name = a.AccountName,
ParentId = a.ParentAccountId
})
.ToListAsync();
var accountIds = RecursiveTreeQueries.GetDescendantIds(treeRows, accountId).ToList();
var lines = _context.JournalEntryLines
.Where(l => l.Journal.IsPosted
&& accountIds.Contains(l.AccountId)
&& l.Journal.EntryDate >= periodStart
&& l.Journal.EntryDate <= periodEnd);
return await BuildGeneralLedgerLinesAsync(lines);
}
public async Task<List<GeneralLedgerLineDto>> GetGeneralLedgerLinesForAllAccountsAsync(DateOnly periodStart, DateOnly periodEnd)
{
var lines = _context.JournalEntryLines
.Where(l => l.Journal.IsPosted
&& l.Account.IsPostable
&& l.Journal.EntryDate >= periodStart
&& l.Journal.EntryDate <= periodEnd);
return await BuildGeneralLedgerLinesAsync(lines);
}
// Orders by account code, then entry date/journal (the running balance restarts for each
// account and accumulates in that same date/journal order), then fetches the rows and
// computes the running balance in memory.
private static async Task<List<GeneralLedgerLineDto>> BuildGeneralLedgerLinesAsync(IQueryable<JournalEntryLine> lines)
{
var rows = await lines
.OrderBy(l => l.Account.AccountCode)
.ThenBy(l => l.Journal.EntryDate)
.ThenBy(l => l.JournalId)
.ThenBy(l => l.LineId)
.Select(l => new
{
l.AccountId,
l.Journal.EntryDate,
l.Journal.JournalNo,
l.Account.AccountCode,
l.Account.AccountName,
l.Journal.Narration,
l.DebitAmount,
l.CreditAmount
})
.ToListAsync();
var runningBalances = new Dictionary<long, decimal>();
var result = new List<GeneralLedgerLineDto>(rows.Count);
foreach (var row in rows)
{
runningBalances.TryGetValue(row.AccountId, out var previous);
var running = previous + row.DebitAmount - row.CreditAmount;
runningBalances[row.AccountId] = running;
result.Add(new GeneralLedgerLineDto
{
EntryDate = row.EntryDate,
JournalNo = row.JournalNo,
AccountCode = row.AccountCode,
AccountName = row.AccountName,
Narration = row.Narration,
DebitAmount = row.DebitAmount,
CreditAmount = row.CreditAmount,
RunningBalance = running
});
}
return result;
}
public Task<JournalCorrection> AddJournalCorrectionAsync(JournalCorrection correction) =>
RepositoryHelpers.AddAsync(_context.JournalCorrections, correction);
}
}