Added the ExpenseUId in expenses tables

This commit is contained in:
ashutosh.nehete 2025-10-04 16:56:04 +05:30
parent f94a7de4ab
commit 04223578ad
11 changed files with 6277 additions and 17 deletions

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,29 @@
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace Marco.Pms.DataAccess.Migrations
{
/// <inheritdoc />
public partial class Added_ExpenceUID_In_Expense_Table : Migration
{
/// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.AddColumn<string>(
name: "ExpenseUId",
table: "Expenses",
type: "longtext",
nullable: false)
.Annotation("MySql:CharSet", "utf8mb4");
}
/// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropColumn(
name: "ExpenseUId",
table: "Expenses");
}
}
}

View File

@ -1832,6 +1832,10 @@ namespace Marco.Pms.DataAccess.Migrations
.IsRequired() .IsRequired()
.HasColumnType("longtext"); .HasColumnType("longtext");
b.Property<string>("ExpenseUId")
.IsRequired()
.HasColumnType("longtext");
b.Property<Guid>("ExpensesTypeId") b.Property<Guid>("ExpensesTypeId")
.HasColumnType("char(36)"); .HasColumnType("char(36)");

View File

@ -54,6 +54,7 @@ namespace Marco.Pms.Model.Expenses
public DateTime CreatedAt { get; set; } public DateTime CreatedAt { get; set; }
public string? TransactionId { get; set; } public string? TransactionId { get; set; }
public string Description { get; set; } = string.Empty; public string Description { get; set; } = string.Empty;
public string ExpenseUId { get; set; } = string.Empty;
public string? Location { get; set; } public string? Location { get; set; }
public string? GSTNumber { get; set; } public string? GSTNumber { get; set; }
public string SupplerName { get; set; } = string.Empty; public string SupplerName { get; set; } = string.Empty;

View File

@ -19,6 +19,7 @@ namespace Marco.Pms.Model.MongoDBModels.Expenses
public DateTime CreatedAt { get; set; } public DateTime CreatedAt { get; set; }
public DateTime ExpireAt { get; set; } = DateTime.UtcNow.Date.AddDays(1); public DateTime ExpireAt { get; set; } = DateTime.UtcNow.Date.AddDays(1);
public string SupplerName { get; set; } = string.Empty; public string SupplerName { get; set; } = string.Empty;
public string? ExpenseUId { get; set; }
public double Amount { get; set; } public double Amount { get; set; }
public ExpensesStatusMasterMongoDB Status { get; set; } = new ExpensesStatusMasterMongoDB(); public ExpensesStatusMasterMongoDB Status { get; set; } = new ExpensesStatusMasterMongoDB();
public List<ExpensesStatusMasterMongoDB> NextStatus { get; set; } = new List<ExpensesStatusMasterMongoDB>(); public List<ExpensesStatusMasterMongoDB> NextStatus { get; set; } = new List<ExpensesStatusMasterMongoDB>();

View File

@ -19,6 +19,7 @@ namespace Marco.Pms.Model.ViewModels.Expenses
public DateTime TransactionDate { get; set; } public DateTime TransactionDate { get; set; }
public DateTime CreatedAt { get; set; } public DateTime CreatedAt { get; set; }
public string SupplerName { get; set; } = string.Empty; public string SupplerName { get; set; } = string.Empty;
public string? ExpenseUId { get; set; }
public double Amount { get; set; } public double Amount { get; set; }
public ExpensesStatusMasterVM? Status { get; set; } public ExpensesStatusMasterVM? Status { get; set; }
public List<ExpensesStatusMasterVM>? NextStatus { get; set; } public List<ExpensesStatusMasterVM>? NextStatus { get; set; }

View File

@ -18,6 +18,7 @@ namespace Marco.Pms.Model.ViewModels.Expanses
public DateTime TransactionDate { get; set; } public DateTime TransactionDate { get; set; }
public DateTime CreatedAt { get; set; } public DateTime CreatedAt { get; set; }
public string SupplerName { get; set; } = string.Empty; public string SupplerName { get; set; } = string.Empty;
public string? ExpenseUId { get; set; }
public string Description { get; set; } = string.Empty; public string Description { get; set; } = string.Empty;
public string TransactionId { get; set; } = string.Empty; public string TransactionId { get; set; } = string.Empty;
public double Amount { get; set; } public double Amount { get; set; }

View File

@ -33,11 +33,11 @@ namespace Marco.Pms.Services.Controllers
#region =================================================================== Contact Get APIs =================================================================== #region =================================================================== Contact Get APIs ===================================================================
[HttpGet("list")] [HttpGet("list")]
public async Task<IActionResult> GetContactList([FromQuery] string? search, [FromQuery] string? filter, [FromQuery] Guid? projectId, [FromQuery] bool active = true, public async Task<IActionResult> GetContactList([FromQuery] string? searchString, [FromQuery] string? filter, [FromQuery] Guid? projectId, [FromQuery] bool active = true,
[FromQuery] int pageNumber = 1, [FromQuery] int pageSize = 20) [FromQuery] int pageNumber = 1, [FromQuery] int pageSize = 20)
{ {
var loggedInEmployee = await _userHelper.GetCurrentEmployeeAsync(); var loggedInEmployee = await _userHelper.GetCurrentEmployeeAsync();
var response = await _directoryService.GetListOfContactsAsync(search: search, filter: filter, projectId: projectId, active: active, pageSize: pageSize, pageNumber: pageNumber, tenantId, loggedInEmployee); var response = await _directoryService.GetListOfContactsAsync(search: searchString, filter: filter, projectId: projectId, active: active, pageSize: pageSize, pageNumber: pageNumber, tenantId, loggedInEmployee);
return StatusCode(response.StatusCode, response); return StatusCode(response.StatusCode, response);

View File

@ -625,10 +625,10 @@ namespace MarcoBMS.Services.Controllers
if (model.Id.HasValue && model.Id.Value != Guid.Empty) if (model.Id.HasValue && model.Id.Value != Guid.Empty)
{ {
existingEmployee = await _context.Employees existingEmployee = await _context.Employees
.FirstOrDefaultAsync(e => e.Id == model.Id && e.OrganizationId == model.OrganizationId); .FirstOrDefaultAsync(e => e.Id == model.Id);
if (existingEmployee == null) if (existingEmployee == null)
{ {
_logger.LogInfo("Employee not found for update. Id={EmployeeId}, Org={OrgId}", model.Id, model.OrganizationId); _logger.LogInfo("Employee not found for update. Id={EmployeeId}", model.Id);
return NotFound(ApiResponse<object>.ErrorResponse("Employee not found", "Employee not found in database", 404)); return NotFound(ApiResponse<object>.ErrorResponse("Employee not found", "Employee not found in database", 404));
} }
} }
@ -986,7 +986,7 @@ namespace MarcoBMS.Services.Controllers
// Update path: fetch scoped to tenant // Update path: fetch scoped to tenant
var employeeId = model.Id.Value; var employeeId = model.Id.Value;
var existingEmployee = await _context.Employees var existingEmployee = await _context.Employees
.FirstOrDefaultAsync(e => e.Id == employeeId && e.TenantId == tenantId); // tenant-safe lookup .FirstOrDefaultAsync(e => e.Id == employeeId); // tenant-safe lookup
if (existingEmployee is null) if (existingEmployee is null)
{ {

View File

@ -1,10 +1,8 @@
using MarcoBMS.Services.Service; using MarcoBMS.Services.Service;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.SignalR; using Microsoft.AspNetCore.SignalR;
namespace Marco.Pms.Services.Hubs namespace Marco.Pms.Services.Hubs
{ {
[Authorize]
public class MarcoHub : Hub public class MarcoHub : Hub
{ {
private readonly ILoggingService _logger; private readonly ILoggingService _logger;

View File

@ -23,6 +23,7 @@ using MarcoBMS.Services.Service;
using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
using System.Text.Json; using System.Text.Json;
using System.Text.RegularExpressions;
using Document = Marco.Pms.Model.DocumentManager.Document; using Document = Marco.Pms.Model.DocumentManager.Document;
namespace Marco.Pms.Services.Service namespace Marco.Pms.Services.Service
@ -487,6 +488,15 @@ namespace Marco.Pms.Services.Service
await using var dbContext = await _dbContextFactory.CreateDbContextAsync(); await using var dbContext = await _dbContextFactory.CreateDbContextAsync();
return await dbContext.PaymentModeMatser.AsNoTracking().FirstOrDefaultAsync(pm => pm.Id == dto.PaymentModeId); return await dbContext.PaymentModeMatser.AsNoTracking().FirstOrDefaultAsync(pm => pm.Id == dto.PaymentModeId);
}); });
var expenseUIdTask = Task.Run(async () =>
{
await using var dbContext = await _dbContextFactory.CreateDbContextAsync();
var result = await dbContext.Expenses
.Where(e => !string.IsNullOrWhiteSpace(e.ExpenseUId)).ToListAsync();
return result
.Select(e => ExtractNumber(e.ExpenseUId))
.OrderByDescending(id => id).FirstOrDefault();
});
var statusMappingTask = Task.Run(async () => var statusMappingTask = Task.Run(async () =>
{ {
await using var dbContext = await _dbContextFactory.CreateDbContextAsync(); await using var dbContext = await _dbContextFactory.CreateDbContextAsync();
@ -506,10 +516,7 @@ namespace Marco.Pms.Services.Service
// Await all prerequisite checks at once. // Await all prerequisite checks at once.
await Task.WhenAll( await Task.WhenAll(hasUploadPermissionTask, hasProjectPermissionTask, projectTask, expenseTypeTask, paymentModeTask, statusMappingTask, paidByTask, expenseUIdTask);
hasUploadPermissionTask, hasProjectPermissionTask,
projectTask, expenseTypeTask, paymentModeTask, statusMappingTask, paidByTask
);
// 2. Aggregate and Check Results // 2. Aggregate and Check Results
if (!await hasUploadPermissionTask || !await hasProjectPermissionTask) if (!await hasUploadPermissionTask || !await hasProjectPermissionTask)
@ -519,11 +526,12 @@ namespace Marco.Pms.Services.Service
} }
var validationErrors = new List<string>(); var validationErrors = new List<string>();
var project = await projectTask; var project = projectTask.Result;
var expenseType = await expenseTypeTask; var expenseType = expenseTypeTask.Result;
var paymentMode = await paymentModeTask; var paymentMode = paymentModeTask.Result;
var statusMapping = await statusMappingTask; var statusMapping = statusMappingTask.Result;
var paidBy = await paidByTask; var paidBy = paidByTask.Result;
var lastExpenseUId = expenseUIdTask.Result;
if (project == null) validationErrors.Add("Project not found."); if (project == null) validationErrors.Add("Project not found.");
if (paidBy == null) validationErrors.Add("Paid by employee not found"); if (paidBy == null) validationErrors.Add("Paid by employee not found");
@ -539,9 +547,10 @@ namespace Marco.Pms.Services.Service
_logger.LogWarning("Expense creation failed due to validation errors: {ValidationErrors}", errorMessage); _logger.LogWarning("Expense creation failed due to validation errors: {ValidationErrors}", errorMessage);
return ApiResponse<object>.ErrorResponse("Invalid input data.", errorMessage, 400); return ApiResponse<object>.ErrorResponse("Invalid input data.", errorMessage, 400);
} }
var currentexpenseUId = (lastExpenseUId + 1).ToString("D5");
// 3. Entity Creation // 3. Entity Creation
var expense = _mapper.Map<Expenses>(dto); var expense = _mapper.Map<Expenses>(dto);
expense.ExpenseUId = $"EX-{currentexpenseUId}";
expense.CreatedById = loggedInEmployee.Id; expense.CreatedById = loggedInEmployee.Id;
expense.CreatedAt = DateTime.UtcNow; expense.CreatedAt = DateTime.UtcNow;
expense.TenantId = tenantId; expense.TenantId = tenantId;
@ -1084,6 +1093,13 @@ namespace Marco.Pms.Services.Service
#endregion #endregion
#region =================================================================== Helper Functions =================================================================== #region =================================================================== Helper Functions ===================================================================
private int ExtractNumber(string id)
{
// Extract trailing number; handles EX_0001, EX-0001, EX0001
var m = Regex.Match(id ?? string.Empty, @"(\d+)$");
return m.Success ? int.Parse(m.Value) : int.MinValue; // put invalid IDs at the bottom
}
private static object ExceptionMapper(Exception ex) private static object ExceptionMapper(Exception ex)
{ {
return new return new