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 CreateAsync(CreateRecurringTemplateRequest request) { if (!Enum.TryParse(request.RecurrencePattern, ignoreCase: true, out var recurrencePattern)) { throw new BadRequestException( $"Invalid recurrencePattern '{request.RecurrencePattern}'. Must be one of: {string.Join(", ", Enum.GetNames())}."); } 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(); 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 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(); 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); } } }