diff --git a/Marco.Pms.Model/ViewModels/Expenses/RecurringPaymentVM.cs b/Marco.Pms.Model/ViewModels/Expenses/RecurringPaymentVM.cs new file mode 100644 index 0000000..b7cc595 --- /dev/null +++ b/Marco.Pms.Model/ViewModels/Expenses/RecurringPaymentVM.cs @@ -0,0 +1,33 @@ +using Marco.Pms.Model.Expenses; +using Marco.Pms.Model.Master; +using Marco.Pms.Model.TenantModels; +using Marco.Pms.Model.ViewModels.Activities; +using Marco.Pms.Model.ViewModels.Master; +using Marco.Pms.Model.ViewModels.Projects; + +namespace Marco.Pms.Model.ViewModels.Expenses +{ + public class RecurringPaymentVM + { + public Guid Id { get; set; } + public string? Title { get; set; } + public string? Description { get; set; } + public string? RecurringPaymentUId { get; set; } + public string? Payee { get; set; } + public string? NotifyTo { get; set; } + public CurrencyMaster? Currency { get; set; } + public double Amount { get; set; } + public DateTime StrikeDate { get; set; } + public DateTime? LatestPRGeneratedAt { get; set; } + public BasicProjectVM? Project { get; set; } + public int PaymentBufferDays { get; set; } + public int NumberOfIteration { get; set; } + public ExpensesCategoryMasterVM? ExpenseCategory { get; set; } + public RecurringPaymentStatus? Status { get; set; } + public PLAN_FREQUENCY Frequency { get; set; } + public bool IsVariable { get; set; } + public bool IsActive { get; set; } + public DateTime CreatedAt { get; set; } + public BasicEmployeeVM? CreatedBy { get; set; } + } +} diff --git a/Marco.Pms.Services/Controllers/ExpenseController.cs b/Marco.Pms.Services/Controllers/ExpenseController.cs index 29071ba..bc08758 100644 --- a/Marco.Pms.Services/Controllers/ExpenseController.cs +++ b/Marco.Pms.Services/Controllers/ExpenseController.cs @@ -222,6 +222,16 @@ namespace Marco.Pms.Services.Controllers return StatusCode(response.StatusCode, response); + } + + [HttpPost("recurring-payment/edit/{id}")] + public async Task EditRecurringPaymentAsync(Guid id, [FromBody] RecurringTemplateDto model) + { + var loggedInEmployee = await _userHelper.GetCurrentEmployeeAsync(); + var response = await _expensesService.EditRecurringPaymentAsync(id, model, loggedInEmployee, tenantId); + + return StatusCode(response.StatusCode, response); + } #endregion diff --git a/Marco.Pms.Services/MappingProfiles/MappingProfile.cs b/Marco.Pms.Services/MappingProfiles/MappingProfile.cs index 7ddf1df..7d33cc2 100644 --- a/Marco.Pms.Services/MappingProfiles/MappingProfile.cs +++ b/Marco.Pms.Services/MappingProfiles/MappingProfile.cs @@ -270,7 +270,7 @@ namespace Marco.Pms.Services.MappingProfiles #region ======================================================= Recurring Request ======================================================= CreateMap(); - //CreateMap(); + CreateMap(); //CreateMap(); #endregion diff --git a/Marco.Pms.Services/Service/ExpensesService.cs b/Marco.Pms.Services/Service/ExpensesService.cs index ffc3bcf..b0ea0bb 100644 --- a/Marco.Pms.Services/Service/ExpensesService.cs +++ b/Marco.Pms.Services/Service/ExpensesService.cs @@ -1943,7 +1943,7 @@ namespace Marco.Pms.Services.Service if (!hasAttachments && paymentRequest.ExpenseCategory!.IsAttachmentRequried) { - _logger.LogWarning("Attachment is required for ExpenseCategory {ExpenseCategoryId} but no attachments provided.", paymentRequest.ExpenseCategoryId); + _logger.LogWarning("Attachment is required for ExpenseCategory {ExpenseCategoryId} but no attachments provided.", paymentRequest.ExpenseCategoryId!); return ApiResponse.ErrorResponse("Attachment is required.", "Attachment is required.", 400); } @@ -1969,7 +1969,7 @@ namespace Marco.Pms.Services.Service var expenseStatuses = expenseStatusTask.Result; // Generate Expense UID with prefix for current period - string uIDPrefix = $"EX/{DateTime.Now:MM.yy}"; + string uIDPrefix = $"EX/{DateTime.Now:MMyy}"; var lastExpense = await _context.Expenses .Where(e => e.UIDPrefix == uIDPrefix) @@ -2067,7 +2067,6 @@ namespace Marco.Pms.Services.Service _logger.LogInfo("End ChangeToExpanseFromPaymentRequestAsync called by EmployeeId: {EmployeeId}", loggedInEmployee.Id); } } - public async Task> EditPaymentRequestAsync(Guid id, PaymentRequestDto model, Employee loggedInEmployee, Guid tenantId) { _logger.LogInfo("Start EditPaymentRequestAsync for PaymentRequestId: {PaymentRequestId}, EmployeeId: {EmployeeId}", id, loggedInEmployee.Id); @@ -2311,8 +2310,98 @@ namespace Marco.Pms.Services.Service #region =================================================================== Recurring Payment Functions =================================================================== public async Task> CreateRecurringPaymentAsync(RecurringTemplateDto model, Employee loggedInEmployee, Guid tenantId) { + using var scope = _serviceScopeFactory.CreateScope(); + var permissionService = scope.ServiceProvider.GetRequiredService(); + var hasPermission = await permissionService.HasPermission(PermissionsMaster.ManageRecurring, loggedInEmployee.Id); + + if (!hasPermission) + { + _logger.LogWarning("Employee {EmployeeId} attempted to create recurring template", loggedInEmployee.Id); + return ApiResponse.ErrorResponse("You do not have access to create recurring template", "You do not have access to create recurring template", 400); + } + + var expenseCategoryTask = Task.Run(async () => + { + await using var context = await _dbContextFactory.CreateDbContextAsync(); + return await context.ExpenseCategoryMasters.FirstOrDefaultAsync(et => et.Id == model.ExpenseCategoryId && et.IsActive); + }); + var currencyTask = Task.Run(async () => + { + await using var context = await _dbContextFactory.CreateDbContextAsync(); + return await context.CurrencyMaster.FirstOrDefaultAsync(c => c.Id == model.CurrencyId); + }); + var projectTask = Task.Run(async () => + { + await using var context = await _dbContextFactory.CreateDbContextAsync(); + return await context.Projects.FirstOrDefaultAsync(P => model.ProjectId.HasValue && P.Id == model.ProjectId.Value); + }); + + await Task.WhenAll(expenseCategoryTask, currencyTask, projectTask); + + var expenseCategory = await expenseCategoryTask; + if (expenseCategory == null) + { + _logger.LogWarning("Expense Category not found with Id: {ExpenseCategoryId}", model.ExpenseCategoryId); + return ApiResponse.ErrorResponse("Expense Category not found.", "Expense Category not found.", 404); + } + + var currency = await currencyTask; + if (currency == null) + { + _logger.LogWarning("Currency not found with Id: {CurrencyId}", model.CurrencyId); + return ApiResponse.ErrorResponse("Currency not found.", "Currency not found.", 404); + } + + var project = await projectTask; + + string uIDPrefix = $"RP/{DateTime.Now:MMyy}"; + + var lastExpense = await _context.RecurringPayments + .Where(e => e.UIDPrefix == uIDPrefix) + .OrderByDescending(e => e.UIDPostfix) + .FirstOrDefaultAsync(); + + int uIDPostfix = lastExpense == null ? 1 : (lastExpense.UIDPostfix + 1); + var recurringPayment = _mapper.Map(model); - return ApiResponse.SuccessResponse(recurringPayment, "Recurring Payment Template created successfully", 201); + recurringPayment.UIDPrefix = uIDPrefix; + recurringPayment.UIDPostfix = uIDPostfix; + recurringPayment.IsActive = true; + recurringPayment.CreatedAt = DateTime.UtcNow; + recurringPayment.CreatedById = loggedInEmployee.Id; + recurringPayment.TenantId = tenantId; + + await _context.SaveChangesAsync(); + + var response = _mapper.Map(recurringPayment); + response.RecurringPaymentUId = $"{recurringPayment.UIDPrefix}/{recurringPayment.UIDPostfix:D5}"; + + return ApiResponse.SuccessResponse(response, "Recurring Payment Template created successfully", 201); + } + + public async Task> EditRecurringPaymentAsync(Guid id, RecurringTemplateDto model, Employee loggedInEmployee, Guid tenantId) + { + string uIDPrefix = $"RP/{DateTime.Now:MMyy}"; + + var lastExpense = await _context.RecurringPayments + .Where(e => e.UIDPrefix == uIDPrefix) + .OrderByDescending(e => e.UIDPostfix) + .FirstOrDefaultAsync(); + + int uIDPostfix = lastExpense == null ? 1 : (lastExpense.UIDPostfix + 1); + + var recurringPayment = _mapper.Map(model); + recurringPayment.UIDPrefix = uIDPrefix; + recurringPayment.UIDPostfix = uIDPostfix; + recurringPayment.IsActive = true; + recurringPayment.CreatedAt = DateTime.UtcNow; + recurringPayment.CreatedById = loggedInEmployee.Id; + recurringPayment.TenantId = tenantId; + + var response = _mapper.Map(recurringPayment); + response.RecurringPaymentUId = $"{recurringPayment.UIDPrefix}/{recurringPayment.UIDPostfix:D5}"; + + return ApiResponse.SuccessResponse(response, "Recurring Payment Template created successfully", 201); } #endregion diff --git a/Marco.Pms.Services/Service/ServiceInterfaces/IExpensesService.cs b/Marco.Pms.Services/Service/ServiceInterfaces/IExpensesService.cs index 2511035..6343e38 100644 --- a/Marco.Pms.Services/Service/ServiceInterfaces/IExpensesService.cs +++ b/Marco.Pms.Services/Service/ServiceInterfaces/IExpensesService.cs @@ -28,13 +28,14 @@ namespace Marco.Pms.Services.Service.ServiceInterfaces Task> EditPaymentRequestAsync(Guid id, PaymentRequestDto model, Employee loggedInEmployee, Guid tenantId); #endregion + #region =================================================================== Recurring Payment Functions =================================================================== + Task> CreateRecurringPaymentAsync(RecurringTemplateDto model, Employee loggedInEmployee, Guid tenantId); + Task> EditRecurringPaymentAsync(Guid id, RecurringTemplateDto model, Employee loggedInEmployee, Guid tenantId); + #endregion #region =================================================================== Advance Payment Functions =================================================================== Task> GetAdvancePaymentTransactionAsync(Guid id); #endregion - #region =================================================================== Recurring Payment Functions =================================================================== - Task> CreateRecurringPaymentAsync(RecurringTemplateDto model, Employee loggedInEmployee, Guid tenantId); - #endregion } }