diff --git a/Marco.Pms.DataAccess/Data/ApplicationDbContext.cs b/Marco.Pms.DataAccess/Data/ApplicationDbContext.cs index 5d2ab7f..946774c 100644 --- a/Marco.Pms.DataAccess/Data/ApplicationDbContext.cs +++ b/Marco.Pms.DataAccess/Data/ApplicationDbContext.cs @@ -34,46 +34,149 @@ namespace Marco.Pms.DataAccess.Data _httpContextAccessor = httpContextAccessor; } - + #region ======================================================= System Masters ======================================================= public DbSet RefreshTokens { get; set; } public DbSet TenantStatus { get; set; } public DbSet SubscriptionStatus { get; set; } + public DbSet Modules { get; set; } + public DbSet Features { get; set; } + public DbSet FeaturePermissions { get; set; } + public DbSet CurrencyMaster { get; set; } + public DbSet Industries { get; set; } + public DbSet Inquiries { get; set; } + public DbSet StatusUpdateLogs { get; set; } + public DbSet PaymentDetails { get; set; } + + #endregion + + #region ======================================================= Tenant Masters ======================================================= + public DbSet Documents { get; set; } + public DbSet MailingList { get; set; } + public DbSet MailDetails { get; set; } + public DbSet MailLogs { get; set; } + public DbSet OTPDetails { get; set; } + public DbSet MPINDetails { get; set; } + public DbSet FCMTokenMappings { get; set; } + + #endregion + + #region ======================================================= Tenant ======================================================= public DbSet Tenants { get; set; } public DbSet SubscriptionPlans { get; set; } public DbSet SubscriptionPlanDetails { get; set; } public DbSet TenantSubscriptions { get; set; } + public DbSet TenantEnquires { get; set; } + + #endregion + + #region ======================================================= Employees ======================================================= + + #region ======================================================= Masters ======================================================= + public DbSet JobRoles { get; set; } + public DbSet ApplicationRoles { get; set; } + public DbSet RolePermissionMappings { get; set; } + #endregion + public DbSet ApplicationUsers { get; set; } + public DbSet Employees { get; set; } + public DbSet EmployeeRoleMappings { get; set; } + #endregion + + #region ======================================================= Project ======================================================= + + #region ======================================================= Masters ======================================================= public DbSet ServiceMasters { get; set; } public DbSet ActivityGroupMasters { get; set; } public DbSet ActivityMasters { get; set; } + public DbSet StatusMasters { get; set; } + public DbSet ActivityCheckLists { get; set; } + public DbSet CheckListMappings { get; set; } + public DbSet WorkCategoryMasters { get; set; } + public DbSet WorkStatusMasters { get; set; } + public DbSet GlobalServiceMasters { get; set; } + public DbSet GlobalActivityGroupMasters { get; set; } + public DbSet GlobalActivityMasters { get; set; } + #endregion + public DbSet Projects { get; set; } public DbSet ProjectAllocations { get; set; } - public DbSet StatusMasters { get; set; } public DbSet Buildings { get; set; } public DbSet Floor { get; set; } public DbSet WorkAreas { get; set; } public DbSet WorkItems { get; set; } public DbSet WorkShifts { get; set; } + public DbSet ProjectLevelPermissionMappings { get; set; } + public DbSet ProjectServiceMappings { get; set; } + + #endregion + + #region ======================================================= Task Allocation ======================================================= public DbSet TaskAllocations { get; set; } public DbSet TaskComments { get; set; } public DbSet TaskMembers { get; set; } public DbSet TaskAttachments { get; set; } + #endregion + + #region ======================================================= Attendace ======================================================= public DbSet Attendes { get; set; } public DbSet AttendanceLogs { get; set; } - public DbSet Employees { get; set; } - public DbSet EmployeeRoleMappings { get; set; } - public DbSet Modules { get; set; } - public DbSet Features { get; set; } - public DbSet FeaturePermissions { get; set; } - public DbSet ProjectLevelPermissionMappings { get; set; } - public DbSet CurrencyMaster { get; set; } - public DbSet ApplicationRoles { get; set; } - public DbSet JobRoles { get; set; } - public DbSet RolePermissionMappings { get; set; } - public DbSet Industries { get; set; } - public DbSet ActivityCheckLists { get; set; } - public DbSet CheckListMappings { get; set; } - public DbSet Inquiries { get; set; } + #endregion + + #region ======================================================= Directory ======================================================= + + #region ======================================================= Masters ======================================================= + public DbSet Buckets { get; set; } + public DbSet ContactTagMasters { get; set; } + public DbSet ContactCategoryMasters { get; set; } + #endregion + + public DbSet Contacts { get; set; } + public DbSet ContactsEmails { get; set; } + public DbSet ContactsPhones { get; set; } + public DbSet ContactNotes { get; set; } + public DbSet ContactTagMappings { get; set; } + public DbSet EmployeeBucketMappings { get; set; } + public DbSet ContactBucketMappings { get; set; } + public DbSet ContactProjectMappings { get; set; } + public DbSet DirectoryUpdateLogs { get; set; } + #endregion + + #region ======================================================= Finance ======================================================= + + #region ======================================================= Masters ======================================================= + public DbSet PaymentModeMatser { get; set; } + public DbSet ExpensesStatusMaster { get; set; } + public DbSet ExpensesTypeMaster { get; set; } + public DbSet ExpenseCategoryMasters { get; set; } + public DbSet RecurringPaymentStatus { get; set; } + + #endregion + + #region ======================================================= Expenses ======================================================= + public DbSet Expenses { get; set; } + public DbSet ExpenseLogs { get; set; } + public DbSet BillAttachments { get; set; } + public DbSet ExpensesReimburse { get; set; } + public DbSet ExpensesReimburseMapping { get; set; } + public DbSet StatusPermissionMapping { get; set; } + public DbSet ExpensesStatusMapping { get; set; } + + #endregion + + #region ======================================================= Collection ======================================================= + public DbSet Invoices { get; set; } + public DbSet InvoiceComments { get; set; } + public DbSet InvoiceAttachments { get; set; } + public DbSet ReceivedInvoicePayments { get; set; } + public DbSet PaymentAdjustmentHeads { get; set; } + #endregion + public DbSet PaymentRequests { get; set; } + public DbSet PaymentRequestAttachments { get; set; } + public DbSet RecurringPayments { get; set; } + public DbSet AdvancePaymentTransactions { get; set; } + #endregion + + #region ======================================================= Tickets ======================================================= public DbSet Tickets { get; set; } public DbSet TicketAttachments { get; set; } public DbSet TicketComments { get; set; } @@ -81,57 +184,10 @@ namespace Marco.Pms.DataAccess.Data public DbSet TicketTypeMasters { get; set; } public DbSet TicketPriorityMasters { get; set; } public DbSet TicketTagMasters { get; set; } - public DbSet Documents { get; set; } public DbSet TicketTags { get; set; } - public DbSet WorkCategoryMasters { get; set; } - public DbSet WorkStatusMasters { get; set; } - public DbSet Contacts { get; set; } - public DbSet ContactCategoryMasters { get; set; } - public DbSet ContactsEmails { get; set; } - public DbSet ContactsPhones { get; set; } - public DbSet ContactNotes { get; set; } - public DbSet Buckets { get; set; } - public DbSet ContactTagMasters { get; set; } - public DbSet ContactTagMappings { get; set; } - public DbSet EmployeeBucketMappings { get; set; } - public DbSet ContactBucketMappings { get; set; } - public DbSet ContactProjectMappings { get; set; } - public DbSet DirectoryUpdateLogs { get; set; } - - public DbSet MailingList { get; set; } - public DbSet MailDetails { get; set; } - public DbSet MailLogs { get; set; } - public DbSet OTPDetails { get; set; } - public DbSet MPINDetails { get; set; } - - public DbSet Expenses { get; set; } - public DbSet ExpenseLogs { get; set; } - public DbSet ExpensesTypeMaster { get; set; } - public DbSet ExpenseCategoryMasters { get; set; } - public DbSet PaymentModeMatser { get; set; } - public DbSet ExpensesStatusMaster { get; set; } - public DbSet BillAttachments { get; set; } - public DbSet ExpensesReimburse { get; set; } - public DbSet ExpensesReimburseMapping { get; set; } - public DbSet StatusPermissionMapping { get; set; } - public DbSet ExpensesStatusMapping { get; set; } - public DbSet PaymentRequests { get; set; } - public DbSet PaymentRequestAttachments { get; set; } - public DbSet RecurringPayments { get; set; } - public DbSet AdvancePaymentTransactions { get; set; } - public DbSet RecurringPaymentStatus { get; set; } - - public DbSet StatusUpdateLogs { get; set; } - - // Collection - public DbSet Invoices { get; set; } - public DbSet InvoiceComments { get; set; } - public DbSet InvoiceAttachments { get; set; } - public DbSet ReceivedInvoicePayments { get; set; } - public DbSet PaymentAdjustmentHeads { get; set; } - - public DbSet FCMTokenMappings { get; set; } + #endregion + #region ======================================================= Project and Employee Documents ======================================================= public DbSet EntityTypeMasters { get; set; } public DbSet DocumentTypeMasters { get; set; } public DbSet DocumentCategoryMasters { get; set; } @@ -139,20 +195,16 @@ namespace Marco.Pms.DataAccess.Data public DbSet DocumentAttachments { get; set; } public DbSet AttachmentVersionMappings { get; set; } public DbSet AttachmentTagMappings { get; set; } + #endregion - public DbSet GlobalServiceMasters { get; set; } - public DbSet GlobalActivityGroupMasters { get; set; } - public DbSet GlobalActivityMasters { get; set; } - + #region ======================================================= Organizations ======================================================= public DbSet Organizations { get; set; } public DbSet OrgTypeMasters { get; set; } public DbSet TenantOrgMappings { get; set; } public DbSet OrgServiceMappings { get; set; } - public DbSet ProjectServiceMappings { get; set; } public DbSet ProjectOrgMappings { get; set; } + #endregion - public DbSet PaymentDetails { get; set; } - public DbSet TenantEnquires { get; set; } protected override void OnModelCreating(ModelBuilder modelBuilder) { diff --git a/Marco.Pms.Model/MongoDBModels/Expenses/ExpenseDetailsMongoDB.cs b/Marco.Pms.Model/MongoDBModels/Expenses/ExpenseDetailsMongoDB.cs index 93b63ba..c1d3f6a 100644 --- a/Marco.Pms.Model/MongoDBModels/Expenses/ExpenseDetailsMongoDB.cs +++ b/Marco.Pms.Model/MongoDBModels/Expenses/ExpenseDetailsMongoDB.cs @@ -24,6 +24,7 @@ namespace Marco.Pms.Model.MongoDBModels.Expenses public CurrencyMaster? Currency { get; set; } public double? BaseAmount { get; set; } public double? TaxAmount { get; set; } + public double? TDSPercentage { get; set; } public string? PaymentRequestUID { get; set; } public string? ExpenseUId { get; set; } public ExpensesStatusMasterMongoDB Status { get; set; } = new ExpensesStatusMasterMongoDB(); diff --git a/Marco.Pms.Model/ViewModels/Expenses/ExpenseDetailsVM.cs b/Marco.Pms.Model/ViewModels/Expenses/ExpenseDetailsVM.cs index c6c35f2..d15c8ea 100644 --- a/Marco.Pms.Model/ViewModels/Expenses/ExpenseDetailsVM.cs +++ b/Marco.Pms.Model/ViewModels/Expenses/ExpenseDetailsVM.cs @@ -21,6 +21,9 @@ namespace Marco.Pms.Model.ViewModels.Expenses public string SupplerName { get; set; } = string.Empty; public string? ExpenseUId { get; set; } public double Amount { get; set; } + public double? BaseAmount { get; set; } + public double? TaxAmount { get; set; } + public double? TDSPercentage { get; set; } public ExpensesStatusMasterVM? Status { get; set; } public List? NextStatus { get; set; } public bool PreApproved { get; set; } = false; diff --git a/Marco.Pms.Model/ViewModels/Expenses/PaymentRequestDetailsVM.cs b/Marco.Pms.Model/ViewModels/Expenses/PaymentRequestDetailsVM.cs index cf994e5..06a5004 100644 --- a/Marco.Pms.Model/ViewModels/Expenses/PaymentRequestDetailsVM.cs +++ b/Marco.Pms.Model/ViewModels/Expenses/PaymentRequestDetailsVM.cs @@ -17,6 +17,7 @@ namespace Marco.Pms.Model.ViewModels.Expenses public double Amount { get; set; } public double? BaseAmount { get; set; } public double? TaxAmount { get; set; } + public double? TDSPercentage { get; set; } public DateTime DueDate { get; set; } public BasicProjectVM? Project { get; set; } public BasicRecurringPaymentVM? RecurringPayment { get; set; } diff --git a/Marco.Pms.Services/Service/ExpensesService.cs b/Marco.Pms.Services/Service/ExpensesService.cs index f0f254f..2149931 100644 --- a/Marco.Pms.Services/Service/ExpensesService.cs +++ b/Marco.Pms.Services/Service/ExpensesService.cs @@ -839,15 +839,22 @@ namespace Marco.Pms.Services.Service $"The sum of the base amount and tax amount ({totalAmount}) does not match the expected expense amount ({expense.Amount}).", 400); } + + var result = ValidateTdsPercentage(model.TDSPercentage); + if (result != null) + { + return result; + } + expense.ProcessedById = loggedInEmployee.Id; expense.BaseAmount = model.BaseAmount; expense.TaxAmount = model.TaxAmount; + expense.TDSPercentage = model.TDSPercentage; } // 7. Add Reimbursement if applicable if (model.StatusId == Processed) { - expense.TDSPercentage = model.TDSPercentage; var reimbursement = new ExpensesReimburse { ReimburseTransactionId = model.ReimburseTransactionId!, @@ -1938,6 +1945,13 @@ namespace Marco.Pms.Services.Service $"The sum of the base amount and tax amount ({totalAmount}) does not match the expected payment request amount ({paymentRequest.Amount}).", 400); } + + var result = ValidateTdsPercentage(model.TDSPercentage); + if (result != null) + { + return result; + } + paymentRequest.PaidAt = model.PaidAt; paymentRequest.PaidById = model.PaidById; paymentRequest.PaidTransactionId = model.PaidTransactionId; @@ -3745,77 +3759,107 @@ namespace Marco.Pms.Services.Service await Task.WhenAll(attachmentTask, documentsTask); } + /// + /// Determines if recurring payments are applicable based on frequency, iteration count, and date logic. + /// + /// Maximum allowed payment iterations. + /// Frequency of recurring payments (e.g., Monthly, Quarterly). + /// The starting date for recurring payments. + /// The date of the latest payment receipt, if any. + /// True if recurring payments are applicable; otherwise, false. private static bool IsRecurringApplicable(int numberOfIteration, PLAN_FREQUENCY frequency, DateTime strikeDate, DateTime? latestPRGeneratedAt) { - List dates = new List(); - DateTime currentDate = strikeDate; - DateTime endDate = DateTime.UtcNow.Date; - - switch (frequency) + // Validate input parameters + if (numberOfIteration <= 0) { - case PLAN_FREQUENCY.MONTHLY: - while (currentDate <= endDate) - { - dates.Add(currentDate); - currentDate = currentDate.AddMonths(1); - } - break; - case PLAN_FREQUENCY.QUARTERLY: - while (currentDate <= endDate) - { - dates.Add(currentDate); - currentDate = currentDate.AddMonths(3); - } - break; - case PLAN_FREQUENCY.HALF_YEARLY: - while (currentDate <= endDate) - { - dates.Add(currentDate); - currentDate = currentDate.AddMonths(6); - } - break; - case PLAN_FREQUENCY.YEARLY: - while (currentDate <= endDate) - { - dates.Add(currentDate); - currentDate = currentDate.AddYears(1); - } - break; - case PLAN_FREQUENCY.DAILY: - while (currentDate <= endDate) - { - dates.Add(currentDate); - currentDate = currentDate.AddDays(1); - } - break; - case PLAN_FREQUENCY.WEEKLY: - while (currentDate <= endDate) - { - dates.Add(currentDate); - currentDate = currentDate.AddDays(7); - } - break; + return false; } + // Ensure strikeDate is in a consistent timezone (UTC or local as per your business logic) + var currentDate = strikeDate.Date; + var endDate = DateTime.UtcNow.Date; + + // List to store generated dates for validation + var dates = new List(); + + // Define increment logic for each frequency + Func incrementFunc = frequency switch + { + PLAN_FREQUENCY.MONTHLY => d => d.AddMonths(1), + PLAN_FREQUENCY.QUARTERLY => d => d.AddMonths(3), + PLAN_FREQUENCY.HALF_YEARLY => d => d.AddMonths(6), + PLAN_FREQUENCY.YEARLY => d => d.AddYears(1), + PLAN_FREQUENCY.DAILY => d => d.AddDays(1), + PLAN_FREQUENCY.WEEKLY => d => d.AddDays(7), + _ => d => d.AddDays(1) + }; + + // Return false if frequency is not supported + if (incrementFunc == null) + { + return false; + } + + // Generate dates based on frequency until endDate + while (currentDate <= endDate) + { + dates.Add(currentDate); + currentDate = incrementFunc(currentDate); + } + + // Validation: Must have at least one date and not exceed iteration count if (!dates.Any() || dates.Count > numberOfIteration) { return false; } + // Validation: Last generated date must match endDate if (dates.Last() != endDate) { return false; } - if (latestPRGeneratedAt.HasValue && latestPRGeneratedAt.Value == endDate) + // Validation: Latest payment receipt should not be on endDate + if (latestPRGeneratedAt.HasValue && latestPRGeneratedAt.Value.Date == endDate) { return false; } return true; - } + /// + /// Validates the TDS Percentage in the provided model. + /// + /// The input model containing TDS Percentage. + /// Returns an error response if validation fails; otherwise, null. + private ApiResponse? ValidateTdsPercentage(double? TDSPercentage) + { + // Check if TDSPercentage is present in the model + if (!TDSPercentage.HasValue) + { + return null; // No validation needed if TDSPercentage is not provided + } + + var tdsValue = TDSPercentage.Value; + + // Validate TDS Percentage range: must be between 0 and 100 inclusive + if (tdsValue < 0 || tdsValue > 100) + { + // Log a warning with structured logging for traceability + _logger.LogWarning("TDS Percentage validation failed. Provided value: {TdsValue} is outside the valid range (0 - 100).", tdsValue); + + // Return a consistent and clear error response with HTTP status 400 + return ApiResponse.ErrorResponse( + "Invalid TDS Percentage value. Allowed range is 0 to 100 inclusive.", + "TDS Percentage value out of range.", + 400); + } + + return null; // Validation successful, no error + } + + #endregion } }