Added the CRUD API for Payment Adjustment heads

This commit is contained in:
ashutosh.nehete 2025-10-15 11:24:48 +05:30
parent 302185808e
commit 8c305872a0
5 changed files with 249 additions and 4 deletions

View File

@ -0,0 +1,9 @@
namespace Marco.Pms.Model.Dtos.Collection
{
public class PaymentAdjustmentHeadDto
{
public Guid? Id { get; set; }
public required string Name { get; set; }
public string? Description { get; set; }
}
}

View File

@ -1,5 +1,6 @@
using Marco.Pms.DataAccess.Data;
using Marco.Pms.Model.Dtos.Activities;
using Marco.Pms.Model.Dtos.Collection;
using Marco.Pms.Model.Dtos.DocumentManager;
using Marco.Pms.Model.Dtos.Master;
using Marco.Pms.Model.Forum;
@ -984,6 +985,29 @@ namespace Marco.Pms.Services.Controllers
var response = await _masterService.GetPaymentAdjustmentHeadListAsync(isActive, loggedInEmployee, tenantId);
return StatusCode(response.StatusCode, response);
}
[HttpPost("payment-adjustment-head")]
public async Task<IActionResult> CreatePaymentAdjustmentHead([FromBody] PaymentAdjustmentHeadDto dto)
{
var loggedInEmployee = await _userHelper.GetCurrentEmployeeAsync();
var response = await _masterService.CreatePaymentAdjustmentHeadAsync(dto, loggedInEmployee, tenantId);
return StatusCode(response.StatusCode, response);
}
[HttpPut("payment-adjustment-head/edit/{id}")]
public async Task<IActionResult> UpdatePaymentAdjustmentHead(Guid id, [FromBody] PaymentAdjustmentHeadDto dto)
{
var loggedInEmployee = await _userHelper.GetCurrentEmployeeAsync();
var response = await _masterService.UpdatePaymentAdjustmentHeadAsync(id, dto, loggedInEmployee, tenantId);
return StatusCode(response.StatusCode, response);
}
[HttpDelete("payment-adjustment-head/delete/{id}")]
public async Task<IActionResult> DeletePaymentAdjustmentHead(Guid id, [FromQuery] bool isActive = false)
{
var loggedInEmployee = await _userHelper.GetCurrentEmployeeAsync();
var response = await _masterService.DeletePaymentAdjustmentHeadAsync(id, isActive, loggedInEmployee, tenantId);
return StatusCode(response.StatusCode, response);
}
#endregion
}
}

View File

@ -269,8 +269,6 @@ namespace Marco.Pms.Services.MappingProfiles
CreateMap<InvoiceComment, InvoiceCommentVM>();
CreateMap<InvoiceAttachment, InvoiceAttachmentVM>();
CreateMap<PaymentAdjustmentHead, PaymentAdjustmentHeadVM>();
#endregion
#region ======================================================= Master =======================================================
@ -402,19 +400,27 @@ namespace Marco.Pms.Services.MappingProfiles
CreateMap<UpdateContactCategoryDto, ContactCategoryMaster>();
CreateMap<ContactCategoryMaster, ContactCategoryVM>();
#endregion
#region ======================================================= Contact Tag Master =======================================================
CreateMap<CreateContactTagDto, ContactTagMaster>();
CreateMap<UpdateContactTagDto, ContactTagMaster>();
CreateMap<ContactTagMaster, ContactTagVM>();
#endregion
#region ======================================================= Payment Adjustment Head Master =======================================================
CreateMap<PaymentAdjustmentHeadDto, PaymentAdjustmentHead>();
CreateMap<PaymentAdjustmentHead, PaymentAdjustmentHeadVM>();
#endregion
#region ======================================================= Expenses Status Master =======================================================
#endregion
#region ======================================================= Expenses Status Master =======================================================
#endregion
#region ======================================================= Expenses Status Master =======================================================
#endregion
#region ======================================================= Expenses Status Master =======================================================
#endregion
#region ======================================================= Expenses Status Master =======================================================
#endregion

View File

@ -1,9 +1,11 @@
using AutoMapper;
using Marco.Pms.DataAccess.Data;
using Marco.Pms.Helpers.Utility;
using Marco.Pms.Model.Collection;
using Marco.Pms.Model.Directory;
using Marco.Pms.Model.DocumentManager;
using Marco.Pms.Model.Dtos.Activities;
using Marco.Pms.Model.Dtos.Collection;
using Marco.Pms.Model.Dtos.DocumentManager;
using Marco.Pms.Model.Dtos.Master;
using Marco.Pms.Model.Employees;
@ -2881,6 +2883,7 @@ namespace Marco.Pms.Services.Service
#endregion
#region =================================================================== Payment Adjustment Head APIs ===================================================================
/// <summary>
/// Retrieves a list of payment adjustment heads for a specific tenant with optional active status filtering.
/// </summary>
@ -2920,6 +2923,205 @@ namespace Marco.Pms.Services.Service
}
}
/// <summary>
/// Creates a new payment adjustment head for the specified tenant after permission and uniqueness checks.
/// </summary>
/// <param name="model">DTO containing payment adjustment head data</param>
/// <param name="loggedInEmployee">The employee performing the action</param>
/// <param name="tenantId">The tenant identifier</param>
/// <returns>API response with status and context</returns>
public async Task<ApiResponse<object>> CreatePaymentAdjustmentHeadAsync(PaymentAdjustmentHeadDto model, Employee loggedInEmployee, Guid tenantId)
{
try
{
// Permission validation
var hasManagePermission = await _permission.HasPermission(PermissionsMaster.ManageMasters, loggedInEmployee.Id);
if (!hasManagePermission)
{
_logger.LogWarning("Access denied for employee {EmployeeId} attempting to create payment adjustment head for tenant {TenantId}.", loggedInEmployee.Id, tenantId);
return ApiResponse<object>.ErrorResponse("Access Denied.", "You do not have permission to manage masters.", 403);
}
// Uniqueness check for payment adjustment head name
var nameExists = await _context.PaymentAdjustmentHeads
.AnyAsync(pah => pah.Name == model.Name && pah.TenantId == tenantId);
if (nameExists)
{
_logger.LogInfo("Duplicate payment adjustment head name '{Name}' detected for tenant {TenantId}.", model.Name, tenantId);
return ApiResponse<object>.ErrorResponse("A payment adjustment head with this name already exists.", "Name of payment adjustment head already exists.", 409);
}
// Create and persist new entity
var paymentAdjustmentHead = _mapper.Map<PaymentAdjustmentHead>(model);
paymentAdjustmentHead.IsActive = true;
paymentAdjustmentHead.TenantId = tenantId;
_context.PaymentAdjustmentHeads.Add(paymentAdjustmentHead);
await _context.SaveChangesAsync();
var response = _mapper.Map<PaymentAdjustmentHeadVM>(paymentAdjustmentHead);
_logger.LogInfo("Payment adjustment head '{Name}' created successfully by employee {EmployeeId} for tenant {TenantId}.", paymentAdjustmentHead.Name, loggedInEmployee.Id, tenantId);
return ApiResponse<object>.SuccessResponse(response, "Payment adjustment head created successfully.", 201);
}
catch (Exception ex)
{
// Log full context with exception for easier debugging
_logger.LogError(ex, "Exception while creating payment adjustment head. Employee: {EmployeeId}, Tenant: {TenantId}, Data: {@Model}", loggedInEmployee.Id, tenantId, model);
return ApiResponse<object>.ErrorResponse("An error occurred.", "Unable to create payment adjustment head at this moment.", 500);
}
}
/// <summary>
/// Updates an existing payment adjustment head for a specified tenant after performing
/// necessary validation, permission, and uniqueness checks.
/// </summary>
/// <param name="id">Unique identifier of the payment adjustment head to update</param>
/// <param name="model">DTO containing updated payment adjustment head data</param>
/// <param name="loggedInEmployee">The employee performing the action</param>
/// <param name="tenantId">The tenant identifier</param>
/// <returns>API response object with update result and status message</returns>
public async Task<ApiResponse<object>> UpdatePaymentAdjustmentHeadAsync(Guid id, PaymentAdjustmentHeadDto model, Employee loggedInEmployee, Guid tenantId)
{
try
{
// --- Step 1: Validate request payload correctness ---
if (!model.Id.HasValue || model.Id != id)
{
_logger.LogWarning("Invalid ID provided in request. Model ID: {ModelId}, Route ID: {RouteId}, TenantId: {TenantId}", model.Id ?? Guid.Empty, id, tenantId);
return ApiResponse<object>.ErrorResponse("Invalid request.", "Provided invalid ID.", 400);
}
// --- Step 2: Validate permissions ---
var hasManagePermission = await _permission.HasPermission(PermissionsMaster.ManageMasters, loggedInEmployee.Id);
if (!hasManagePermission)
{
_logger.LogWarning("Access denied for employee {EmployeeId} attempting to update payment adjustment head for tenant {TenantId}.", loggedInEmployee.Id, tenantId);
return ApiResponse<object>.ErrorResponse("Access Denied.", "You do not have permission to manage masters.", 403);
}
// --- Step 3: Validate uniqueness constraint for name ---
var nameExists = await _context.PaymentAdjustmentHeads
.AnyAsync(pah => pah.Name == model.Name && pah.Id != id && pah.TenantId == tenantId);
if (nameExists)
{
_logger.LogInfo("Duplicate payment adjustment head name '{Name}' detected during update for tenant {TenantId}.", model.Name, tenantId);
return ApiResponse<object>.ErrorResponse("Conflict detected.", "A payment adjustment head with this name already exists.", 409);
}
// --- Step 4: Retrieve and validate existing entity ---
var paymentAdjustmentHead = await _context.PaymentAdjustmentHeads
.FirstOrDefaultAsync(pah => pah.Id == id && pah.TenantId == tenantId);
if (paymentAdjustmentHead == null)
{
_logger.LogWarning("Payment adjustment head with ID {Id} not found for tenant {TenantId}.", id, tenantId);
return ApiResponse<object>.ErrorResponse("Not Found.", "Payment adjustment head not found.", 404);
}
// Mapping PaymentAdjustmentHead to BsonDocument
var existingEntityBson = _updateLogHelper.EntityToBsonDocument(paymentAdjustmentHead);
// --- Step 5: Map changes and update entity ---
_mapper.Map(model, paymentAdjustmentHead);
await _context.SaveChangesAsync();
await _updateLogHelper.PushToUpdateLogsAsync(new UpdateLogsObject
{
EntityId = paymentAdjustmentHead.Id.ToString(),
UpdatedById = loggedInEmployee.Id.ToString(),
OldObject = existingEntityBson,
UpdatedAt = DateTime.UtcNow
}, "PaymentAdjustmentHeadModificationLog");
_logger.LogInfo("Payment adjustment head '{Name}' updated successfully by employee {EmployeeId} for tenant {TenantId}.", paymentAdjustmentHead.Name, loggedInEmployee.Id, tenantId);
var response = _mapper.Map<PaymentAdjustmentHeadVM>(paymentAdjustmentHead);
// --- Step 6: Return structured success response ---
return ApiResponse<object>.SuccessResponse(response, "Payment adjustment head updated successfully.", 200);
}
catch (Exception ex)
{
// --- Step 7: Handle and log exceptions with full context ---
_logger.LogError(ex, "Exception while updating payment adjustment head. Employee: {EmployeeId}, Tenant: {TenantId}, Model: {@Model}", loggedInEmployee.Id, tenantId, model);
return ApiResponse<object>.ErrorResponse("An unexpected error occurred.", "Unable to update payment adjustment head at this moment.", 500);
}
}
/// <summary>
/// Activates or deactivates a payment adjustment head (soft delete/restore) for a tenant,
/// including audit logging and permission validation.
/// </summary>
/// <param name="id">Unique identifier of the payment adjustment head</param>
/// <param name="isActive">Flag indicating activation (restore) or deactivation (delete)</param>
/// <param name="loggedInEmployee">Employee requesting the operation</param>
/// <param name="tenantId">The tenant identifier</param>
/// <returns>API response object with operation status</returns>
public async Task<ApiResponse<object>> DeletePaymentAdjustmentHeadAsync(Guid id, bool isActive, Employee loggedInEmployee, Guid tenantId)
{
// Dynamically select operation word for logs and messages
var operation = isActive ? "restore" : "delete";
try
{
// Step 1: Permission check
var hasManagePermission = await _permission.HasPermission(PermissionsMaster.ManageMasters, loggedInEmployee.Id);
if (!hasManagePermission)
{
_logger.LogWarning("Access denied: Employee {EmployeeId} attempted to {Operation} payment adjustment head for tenant {TenantId}.",
loggedInEmployee.Id, operation, tenantId);
return ApiResponse<object>.ErrorResponse("Access Denied.", "You do not have permission to manage masters.", 403);
}
// Step 2: Entity existence check
var paymentAdjustmentHead = await _context.PaymentAdjustmentHeads
.FirstOrDefaultAsync(pah => pah.Id == id && pah.TenantId == tenantId);
if (paymentAdjustmentHead == null)
{
_logger.LogWarning("Payment adjustment head with ID {Id} not found for tenant {TenantId} (attempted {Operation}).",
id, tenantId, operation);
return ApiResponse<object>.ErrorResponse("Not Found.", "Payment adjustment head not found.", 404);
}
// Step 3: Save pre-update state for audit log
var existingEntityBson = _updateLogHelper.EntityToBsonDocument(paymentAdjustmentHead);
// Step 4: Update IsActive status
paymentAdjustmentHead.IsActive = isActive;
await _context.SaveChangesAsync();
// Step 5: Push update action to audit log
await _updateLogHelper.PushToUpdateLogsAsync(new UpdateLogsObject
{
EntityId = paymentAdjustmentHead.Id.ToString(),
UpdatedById = loggedInEmployee.Id.ToString(),
OldObject = existingEntityBson,
UpdatedAt = DateTime.UtcNow
}, "PaymentAdjustmentHeadModificationLog");
_logger.LogInfo(
"Payment adjustment head (ID: {Id}, Name: {Name}) successfully {Operation}d by employee {EmployeeId} for tenant {TenantId}.",
paymentAdjustmentHead.Id, paymentAdjustmentHead.Name, operation, loggedInEmployee.Id, tenantId);
return ApiResponse<object>.SuccessResponse(new { }, $"Payment adjustment head {operation}d successfully.", 200);
}
catch (Exception ex)
{
_logger.LogError(ex,
"Exception occurred while performing {Operation} on payment adjustment head (ID: {Id}) for tenant {TenantId}.",
operation, id, tenantId);
return ApiResponse<object>.ErrorResponse("An error occurred.", $"Exception occurred while trying to {operation} payment adjustment head.", 500);
}
}
#endregion
#region =================================================================== Helper Function ===================================================================

View File

@ -1,4 +1,5 @@
using Marco.Pms.Model.Dtos.Activities;
using Marco.Pms.Model.Dtos.Collection;
using Marco.Pms.Model.Dtos.DocumentManager;
using Marco.Pms.Model.Dtos.Master;
using Marco.Pms.Model.Employees;
@ -108,6 +109,9 @@ namespace Marco.Pms.Services.Service.ServiceInterfaces
#region =================================================================== Payment Adjustment Head APIs ===================================================================
Task<ApiResponse<object>> GetPaymentAdjustmentHeadListAsync(bool isActive, Employee loggedInEmployee, Guid tenantId);
Task<ApiResponse<object>> CreatePaymentAdjustmentHeadAsync(PaymentAdjustmentHeadDto model, Employee loggedInEmployee, Guid tenantId);
Task<ApiResponse<object>> UpdatePaymentAdjustmentHeadAsync(Guid id, PaymentAdjustmentHeadDto model, Employee loggedInEmployee, Guid tenantId);
Task<ApiResponse<object>> DeletePaymentAdjustmentHeadAsync(Guid id, bool isActive, Employee loggedInEmployee, Guid tenantId);
#endregion
}
}