Files
Distribution_GL/Services/RecurringTemplateService.cs
T

172 lines
6.8 KiB
C#

using General_Ledger_Service.DTOs.Common;
using General_Ledger_Service.DTOs.JournalEntries;
using General_Ledger_Service.DTOs.RecurringTemplates;
using General_Ledger_Service.Enums;
using General_Ledger_Service.Exceptions;
using General_Ledger_Service.Infra.UoW;
using General_Ledger_Service.Interfaces.Repositories;
using General_Ledger_Service.Interfaces.Services;
using General_Ledger_Service.Models;
namespace General_Ledger_Service.Services
{
public class RecurringTemplateService(
IRecurringJournalTemplateRepository templateRepository,
IAccountRepository accountRepository,
IFiscalCalendarRepository fiscalCalendarRepository,
IJournalEntryService journalEntryService,
IAuditLogRepository auditLogRepository,
IUnitOfWork unitOfWork) : IRecurringTemplateService
{
private readonly IRecurringJournalTemplateRepository _templateRepository = templateRepository;
private readonly IAccountRepository _accountRepository = accountRepository;
private readonly IFiscalCalendarRepository _fiscalCalendarRepository = fiscalCalendarRepository;
private readonly IJournalEntryService _journalEntryService = journalEntryService;
private readonly IAuditLogRepository _auditLogRepository = auditLogRepository;
private readonly IUnitOfWork _unitOfWork = unitOfWork;
public async Task<ApiResponse> CreateAsync(CreateRecurringTemplateRequest request)
{
if (!Enum.TryParse<RecurrencePattern>(request.RecurrencePattern, ignoreCase: true, out var recurrencePattern))
{
throw new BadRequestException(
$"Invalid recurrencePattern '{request.RecurrencePattern}'. Must be one of: {string.Join(", ", Enum.GetNames<RecurrencePattern>())}.");
}
if (request.Lines.Count == 0)
{
throw new BadRequestException("Recurring template must have at least one line");
}
var totalDebit = request.Lines.Sum(l => l.DebitAmount);
var totalCredit = request.Lines.Sum(l => l.CreditAmount);
if (totalDebit != totalCredit)
{
throw new BadRequestException("Recurring template lines do not balance");
}
var lines = new List<RecurringJournalTemplateLine>();
foreach (var lineRequest in request.Lines)
{
var account = await _accountRepository.GetByCodeAsync(lineRequest.AccountCode)
?? throw new NotFoundException($"Account {lineRequest.AccountCode} not found");
if (!account.IsPostable)
{
throw new BadRequestException($"Account {account.AccountCode} is not postable");
}
lines.Add(new RecurringJournalTemplateLine
{
AccountId = account.AccountId,
DebitAmount = lineRequest.DebitAmount,
CreditAmount = lineRequest.CreditAmount
});
}
var template = new RecurringJournalTemplate
{
TemplateName = request.TemplateName,
RecurrencePattern = recurrencePattern,
NextRunDate = request.NextRunDate,
EndDate = request.EndDate,
IsActive = true,
Lines = lines
};
await _unitOfWork.BeginAsync();
try
{
await _templateRepository.AddAsync(template);
await _unitOfWork.SaveChangesAsync();
await _auditLogRepository.AddAsync(new AuditLog
{
Category = AuditCategory.Administrative,
EntityName = "RecurringJournalTemplate",
EntityId = template.TemplateId.ToString(),
Action = AuditAction.Create,
Description = $"Recurring template {template.TemplateName} created",
PerformedBy = null
});
await _unitOfWork.SaveChangesAsync();
await _unitOfWork.CommitAsync();
}
catch
{
await _unitOfWork.RollbackAsync();
throw;
}
return ResponseHelpers.Created("Recurring template created successfully", template);
}
public async Task<ApiResponse> GenerateAsync(long templateId)
{
var template = await _templateRepository.GetByIdAsync(templateId)
?? throw new NotFoundException("Recurring template not found");
if (!template.IsActive)
{
throw new BadRequestException("Recurring template is not active");
}
var today = DateOnly.FromDateTime(DateTime.UtcNow);
if (template.NextRunDate > today)
{
throw new BadRequestException("Recurring template is not yet due to run");
}
var period = await _fiscalCalendarRepository.GetPeriodByDateAsync(today)
?? throw new NotFoundException("No accounting period covers today's date");
var lines = new List<JournalEntryLineRequest>();
foreach (var line in template.Lines)
{
var account = await _accountRepository.GetByIdAsync(line.AccountId);
lines.Add(new JournalEntryLineRequest
{
AccountCode = account!.AccountCode,
DebitAmount = line.DebitAmount,
CreditAmount = line.CreditAmount
});
}
var journalRequest = new CreateJournalEntryRequest
{
PeriodId = period.PeriodId,
EntryDate = today,
JournalType = "RECURRING",
Reference = template.TemplateName,
Narration = $"Generated from recurring template {template.TemplateName}",
Lines = lines
};
var journalResponse = await _journalEntryService.CreateAsync(journalRequest);
template.NextRunDate = template.RecurrencePattern switch
{
RecurrencePattern.Monthly => template.NextRunDate.AddMonths(1),
RecurrencePattern.Quarterly => template.NextRunDate.AddMonths(3),
RecurrencePattern.Annual => template.NextRunDate.AddYears(1),
_ => template.NextRunDate
};
await _unitOfWork.BeginAsync();
try
{
await _unitOfWork.SaveChangesAsync();
await _unitOfWork.CommitAsync();
}
catch
{
await _unitOfWork.RollbackAsync();
throw;
}
return ResponseHelpers.Created("Recurring journal entry generated successfully", journalResponse.Data);
}
}
}