Distribution_GL Initialization
This commit is contained in:
@@ -0,0 +1,171 @@
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user