using Marco.Pms.DataAccess.Data; using Marco.Pms.Model.Dtos.Expenses; using Marco.Pms.Model.Entitlements; using Marco.Pms.Model.Expenses; using Marco.Pms.Model.Utilities; using Marco.Pms.Services.Service; using MarcoBMS.Services.Helpers; using MarcoBMS.Services.Service; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; using Microsoft.EntityFrameworkCore; using Document = Marco.Pms.Model.DocumentManager.Document; // For more information on enabling Web API for empty projects, visit https://go.microsoft.com/fwlink/?LinkID=397860 namespace Marco.Pms.Services.Controllers { [Route("api/[controller]")] [ApiController] [Authorize] public class ExpanseController : ControllerBase { private readonly ApplicationDbContext _context; private readonly UserHelper _userHelper; private readonly PermissionServices _permission; private readonly ILoggingService _logger; private readonly S3UploadService _s3Service; private readonly Guid tenantId; public ExpanseController( ApplicationDbContext context, UserHelper userHelper, PermissionServices permission, ILoggingService logger, S3UploadService s3Service) { _context = context; _userHelper = userHelper; _permission = permission; _logger = logger; tenantId = userHelper.GetTenantId(); _s3Service = s3Service; } [HttpGet] public IEnumerable Get() { return new string[] { "value1", "value2" }; } [HttpGet("{id}")] public string Get(int id) { return "value"; } [HttpPost] public async Task Post([FromBody] CreateExpensesDto dto) { var loggedInEmployee = await _userHelper.GetCurrentEmployeeAsync(); var hasUploadPermission = await _permission.HasPermission(PermissionsMaster.ExpenseUpload, loggedInEmployee.Id); var hasProjectPermission = await _permission.HasProjectPermission(loggedInEmployee, dto.ProjectId); if (!hasUploadPermission || !hasProjectPermission) { _logger.LogWarning("Access DENIED for employee {EmployeeId} for uploading expense on project {ProjectId}.", loggedInEmployee.Id, dto.ProjectId); return StatusCode(403, ApiResponse.ErrorResponse("Access Denied.", "You do not have permission to Upload expenses for this project", 403)); } var isExpensesTypeExist = await _context.ExpensesTypeMaster.AnyAsync(et => et.Id == dto.ExpensesTypeId); if (!isExpensesTypeExist) { _logger.LogWarning("Expenses type not for ID: {ExpensesTypeId} when creating new expense", dto.ExpensesTypeId); return NotFound(ApiResponse.ErrorResponse("Expanses Type not found", "Expanses Type not found", 404)); } var isPaymentModeExist = await _context.PaymentModeMatser.AnyAsync(et => et.Id == dto.PaymentModeId); if (!isPaymentModeExist) { _logger.LogWarning("Payment Mode not for ID: {PaymentModeId} when creating new expense", dto.PaymentModeId); return NotFound(ApiResponse.ErrorResponse("Payment Mode not found", "Payment Mode not found", 404)); } var isStatusExist = await _context.ExpensesStatusMaster.AnyAsync(et => et.Id == dto.StatusId); if (!isStatusExist) { _logger.LogWarning("Status not for ID: {PaymentModeId} when creating new expense", dto.PaymentModeId); return NotFound(ApiResponse.ErrorResponse("Status not found", "Status not found", 404)); } var expense = new Expenses { ProjectId = dto.ProjectId, ExpensesTypeId = dto.ExpensesTypeId, PaymentModeId = dto.PaymentModeId, PaidById = dto.PaidById, CreatedById = loggedInEmployee.Id, TransactionDate = dto.TransactionDate, CreatedAt = DateTime.UtcNow, TransactionId = dto.TransactionId, Description = dto.Description, Location = dto.Location, GSTNumber = dto.GSTNumber, SupplerName = dto.SupplerName, Amount = dto.Amount, NoOfPersons = dto.NoOfPersons, StatusId = dto.StatusId, PreApproved = dto.PreApproved, IsActive = true, TenantId = tenantId }; _context.Expenses.Add(expense); Guid batchId = Guid.NewGuid(); foreach (var attachment in dto.BillAttachments) { //if (!_s3Service.IsBase64String(attachment.Base64Data)) //{ // _logger.LogWarning("Image upload failed: Base64 data is missing While creating new expense entity for project {ProjectId} by employee {EmployeeId}", expense.ProjectId, expense.PaidById); // return BadRequest(ApiResponse.ErrorResponse("Base64 data is missing", "Base64 data is missing", 400)); //} var base64 = attachment.Base64Data!.Contains(',') ? attachment.Base64Data[(attachment.Base64Data.IndexOf(",") + 1)..] : attachment.Base64Data; var fileType = _s3Service.GetContentTypeFromBase64(base64); var fileName = _s3Service.GenerateFileName(fileType, expense.Id, "Expense"); var objectKey = $"tenant-{tenantId}/project-{expense.ProjectId}/Expenses/{fileName}"; try { await _s3Service.UploadFileAsync(base64, fileType, objectKey); _logger.LogInfo("Image uploaded to S3 with key: {ObjectKey}", objectKey); } catch (Exception ex) { _logger.LogError(ex, "Error occured while saving image to S3"); //return BadRequest(ApiResponse.ErrorResponse("Cannot upload attachment to S3", new //{ // message = ex.Message, // innerexcption = ex.InnerException?.Message, // stackTrace = ex.StackTrace, // source = ex.Source //}, 400)); } var document = new Document { BatchId = batchId, UploadedById = loggedInEmployee.Id, FileName = attachment.FileName ?? "", ContentType = attachment.ContentType ?? "", S3Key = objectKey, //Base64Data = attachment.Base64Data, FileSize = attachment.FileSize, UploadedAt = DateTime.UtcNow, TenantId = tenantId }; _context.Documents.Add(document); var billAttachement = new BillAttachments { DocumentId = document.Id, ExpensesId = expense.Id, TenantId = tenantId }; _context.BillAttachments.Add(billAttachement); } try { await _context.SaveChangesAsync(); } catch (DbUpdateException dbEx) { _logger.LogError(dbEx, "Error occured while saving Expense, Document and bill attachment entity"); return BadRequest(ApiResponse.ErrorResponse("Databsae Exception", new { Message = dbEx.Message, StackTrace = dbEx.StackTrace, Source = dbEx.Source, innerexcption = new { Message = dbEx.InnerException?.Message, StackTrace = dbEx.InnerException?.StackTrace, Source = dbEx.InnerException?.Source, } }, 400)); } _logger.LogInfo("Documents and attachments saved for Expense: {ExpenseId}", expense.Id); return StatusCode(201, ApiResponse.SuccessResponse(expense, "Expense created Successfully", 201)); } [HttpPut("{id}")] public void Put(int id, [FromBody] string value) { } [HttpDelete("{id}")] public void Delete(int id) { } } }