diff --git a/.vs/OSS/v16/.suo b/.vs/OSS/v16/.suo index b8a267b..f3eac74 100644 Binary files a/.vs/OSS/v16/.suo and b/.vs/OSS/v16/.suo differ diff --git a/Controllers/NewCOIController.cs b/Controllers/NewCOIController.cs index 32836cc..cc13943 100644 --- a/Controllers/NewCOIController.cs +++ b/Controllers/NewCOIController.cs @@ -1900,6 +1900,41 @@ namespace OSS.Controllers } + private void EnsurePaymentSessionDefaults(string currencyIfUnset = "") + { + if (Session["Currency"] == null) Session["Currency"] = currencyIfUnset; + if (Session["Amount"] == null) Session["Amount"] = ""; + if (Session["AmountinWords"] == null) Session["AmountinWords"] = ""; + } + + private bool TrySaveInvoice(OSSDBContext ctx, ApplicationManager inv, CompanyProfileExternal company, out string error) + { + error = null; + try + { + ctx.ApplicationManagers.Add(inv); + if (company != null) + { + company.CleaningStatus = "Submitted"; + } + ctx.SaveChanges(); + return true; + } + catch (System.Data.Entity.Validation.DbEntityValidationException ex) + { + var msgs = ex.EntityValidationErrors + .SelectMany(e => e.ValidationErrors) + .Select(e => e.PropertyName + ": " + e.ErrorMessage); + error = "Validation failed: " + string.Join("; ", msgs); + return false; + } + catch (Exception ex) + { + error = ex.Message; + return false; + } + } + public ActionResult Payment(ApplicationManager model) { try @@ -1912,12 +1947,56 @@ namespace OSS.Controllers if (string.IsNullOrWhiteSpace(model?.Currency)) { ViewBag.Error = "Currency is required."; + EnsurePaymentSessionDefaults(); return View("Payment"); } var Username = Session["CompanyEmail"].ToString(); var ProjectCode = Session["ProjectCode"].ToString(); - var serviceName = (Session["ServiceName"] as string) ?? string.Empty; + var serviceName = ((Session["ServiceName"] as string) ?? string.Empty).Trim(); + + // Normalize service name to match tblService keys (e.g., "Application for New Certificate" -> "New") + var rawService = serviceName; + if (rawService.IndexOf("new", StringComparison.OrdinalIgnoreCase) >= 0) + { + serviceName = "New"; + } + else if (rawService.IndexOf("expansion", StringComparison.OrdinalIgnoreCase) >= 0) + { + serviceName = "Expansion"; + } + + // Enforce TZS only as requested + model.Currency = "TZS"; + + // Pre-compute amount from tblService (before early returns), single-row fetch (no iteration) + var getPDetailsEarly = myContext.ProjectProfilesExternal.SingleOrDefault(t => t.ProjectCode == ProjectCode); + if (getPDetailsEarly != null) + { + var ownershipRawEarly = (getPDetailsEarly.TypeofOwnership ?? "").Trim(); + var isLocalEarly = ownershipRawEarly.Equals("Local", StringComparison.OrdinalIgnoreCase); + var isForeignOrMixedEarly = + ownershipRawEarly.Equals("Foreign", StringComparison.OrdinalIgnoreCase) || + ownershipRawEarly.Equals("JV", StringComparison.OrdinalIgnoreCase) || + ownershipRawEarly.Equals("Mixed", StringComparison.OrdinalIgnoreCase) || + ownershipRawEarly.Equals("Mixed (Foreigners & Tanzanians)", StringComparison.OrdinalIgnoreCase); + + var serviceKeyEarly = serviceName; + if (!isLocalEarly && isForeignOrMixedEarly) serviceKeyEarly = serviceName + "_Foreign"; + + var feeRow = myContext.ServiceFees.FirstOrDefault(s => + + s.ServiceName == serviceKeyEarly); + + if (feeRow != null && feeRow.Fee.HasValue && feeRow.Fee.Value > 0) + { + TempData["PreAmountTZS"] = feeRow.Fee.Value.ToString("0"); + } + else + { + TempData["PreAmountTZS"] = null; // force UI error downstream if not configured + } + } var checkIfExist = myContext.ApplicationManagers.SingleOrDefault(t => t.ProjectCode == ProjectCode); var checkIfCompany = myContext.CompanyProfileExternal.SingleOrDefault(t => t.AddedBy == Username); @@ -1928,6 +2007,16 @@ namespace OSS.Controllers if (getPDetails == null) { ViewBag.Error = "Project details not found."; + EnsurePaymentSessionDefaults(); + return View("Payment"); + } + + // Only New/Expansion supported in this TZS ownership block + if (!(serviceName.Equals("New", StringComparison.OrdinalIgnoreCase) || + serviceName.Equals("Expansion", StringComparison.OrdinalIgnoreCase))) + { + ViewBag.Error = $"Unsupported ServiceName '{serviceName}'. Expected New/Expansion."; + EnsurePaymentSessionDefaults(); return View("Payment"); } @@ -1951,116 +2040,46 @@ namespace OSS.Controllers inv.Station = model.Station; inv.CompanyTIN = Session["CompanyTIN"] != null ? Session["CompanyTIN"].ToString() : null; - // Determine service name and try dynamic fee from tblService (case-insensitive) - var svcFee = myContext.ServiceFees - .AsEnumerable() - .FirstOrDefault(s => - s.Status == 1 && - string.Equals(s.ServiceName, serviceName, StringComparison.OrdinalIgnoreCase) && - string.Equals(s.Currency, model.Currency, StringComparison.OrdinalIgnoreCase)); - - // Ownership-based TZS pricing for New and Expansion only (drive from tblService) - if (string.Equals(model.Currency, "TZS", StringComparison.OrdinalIgnoreCase) - && (serviceName.Equals("New", StringComparison.OrdinalIgnoreCase) || serviceName.Equals("Expansion", StringComparison.OrdinalIgnoreCase))) + // Use precomputed amount from tblService or fallback (saved in TempData earlier) + decimal amountToUse; + if (TempData["PreAmountTZS"] != null && decimal.TryParse(TempData["PreAmountTZS"].ToString(), out amountToUse)) { - var ownership = (getPDetails.TypeofOwnership ?? "").Trim(); - - // Pick service key based on ownership - var serviceKey = serviceName; - if (!ownership.Equals("Local", StringComparison.OrdinalIgnoreCase)) - { - serviceKey = serviceName + "_Foreign"; - } - - // Try to fetch fee from tblService, case-insensitive - var ownershipFee = myContext.ServiceFees - .AsEnumerable() - .FirstOrDefault(s => - s.Status == 1 && - string.Equals(s.ServiceName, serviceKey, StringComparison.OrdinalIgnoreCase) && - s.Currency.Equals("TZS", StringComparison.OrdinalIgnoreCase)); - - if (ownershipFee?.Fee == null) - { - ViewBag.Error = $"Configured fee not found for '{serviceKey}' in TZS."; - return View("Payment"); - } - - var amountTzs = ownershipFee.Fee.Value; - inv.Amount = amountTzs; - Session["Amount"] = amountTzs.ToString("0"); - Session["AmountinWords"] = CurrencyUtils.ToWords(amountTzs) + " Tanzanian Shillings"; + inv.Amount = amountToUse; + Session["Amount"] = amountToUse.ToString("0"); + Session["AmountinWords"] = CurrencyUtils.ToWords(amountToUse) + " Tanzanian Shillings"; Session["Currency"] = model.Currency; - - myContext.ApplicationManagers.Add(inv); - if (checkIfCompany != null) - { - checkIfCompany.CleaningStatus = "Submitted"; - } - myContext.SaveChanges(); - - if (serviceName.Equals("Expansion", StringComparison.OrdinalIgnoreCase)) - { - return View("PaymentExpansion"); - } - return View("Payment"); - } - - // If not TZS New/Expansion, use tblService if available - if (svcFee?.Fee != null) - { - var fee = svcFee.Fee.Value; - inv.Amount = fee; - Session["Amount"] = fee.ToString("0.##"); - Session["AmountinWords"] = CurrencyUtils.ToWords(fee) + " " + model.Currency; - Session["Currency"] = model.Currency; - - myContext.ApplicationManagers.Add(inv); - if (checkIfCompany != null) - { - checkIfCompany.CleaningStatus = "Submitted"; - } - myContext.SaveChanges(); - - if (serviceName.Equals("Expansion", StringComparison.OrdinalIgnoreCase)) - { - return View("PaymentExpansion"); - } - return View("Payment"); - } - - // Fallback: existing COIPrice + ExchangeRate for New - ExchangeRate exchangeRate = myContext.ExchangeRates.SingleOrDefault(t => t.Currency == model.Currency); - COIPrice coiPrice = myContext.COIPrices.SingleOrDefault(t => t.ApplicationType == "New"); - if (exchangeRate != null && coiPrice != null) - { - decimal Price = coiPrice.Price * exchangeRate.Rate; - string priceStr = Price.ToString(); - priceStr = priceStr.Contains(".") ? priceStr.TrimEnd('0').TrimEnd('.') : priceStr; - - inv.Amount = Price; - Session["Amount"] = priceStr; - Session["AmountinWords"] = CurrencyUtils.ToWords(Price) + " " + exchangeRate.CurrencyDesc; - Session["Currency"] = model.Currency; - - myContext.ApplicationManagers.Add(inv); - if (checkIfCompany != null) - { - checkIfCompany.CleaningStatus = "Submitted"; - } - myContext.SaveChanges(); - - if (serviceName.Equals("Expansion", StringComparison.OrdinalIgnoreCase)) - { - return View("PaymentExpansion"); - } - return View("Payment"); } else { - ViewBag.Error = "Unable to determine fee for the selected currency."; + var ownershipForKey = getPDetails.TypeofOwnership != null && getPDetails.TypeofOwnership.Equals("Local", StringComparison.OrdinalIgnoreCase) + ? serviceName + : serviceName + "_Foreign"; + + var errMsg = $"Fee not configured in tblService for ServiceName='{ownershipForKey}', Currency='TZS'."; + TempData["PaymentError"] = errMsg; + ViewBag.Error = errMsg; + EnsurePaymentSessionDefaults("TZS"); + // Show the appropriate payment page based on service + if (serviceName.Equals("Expansion", StringComparison.OrdinalIgnoreCase)) + { + return View("PaymentExpansion"); + } return View("Payment"); } + + string saveErrX; + if (!TrySaveInvoice(myContext, inv, checkIfCompany, out saveErrX)) + { + ViewBag.Error = "Could not save invoice. " + saveErrX; + EnsurePaymentSessionDefaults(model.Currency); + return View("Payment"); + } + + if (serviceName.Equals("Expansion", StringComparison.OrdinalIgnoreCase)) + { + return View("PaymentExpansion"); + } + return View("Payment"); } else { @@ -2074,6 +2093,7 @@ namespace OSS.Controllers catch (Exception ex) { ViewBag.Error = "Error processing payment. " + ex.Message; + EnsurePaymentSessionDefaults(); return View("Payment"); } } diff --git a/Models/ServiceFee.cs b/Models/ServiceFee.cs index 30dfe63..5c88361 100644 --- a/Models/ServiceFee.cs +++ b/Models/ServiceFee.cs @@ -10,9 +10,9 @@ namespace OSS.Models public class ServiceFee { [Key] - public int ServiID { get; set; } + public long ServiID { get; set; } public string ServiceName { get; set; } - public int Status { get; set; } + public string Status { get; set; } public decimal? Fee { get; set; } public string GFSCode { get; set; } public string Currency { get; set; } diff --git a/bin/OSS.dll b/bin/OSS.dll index 4702952..ec2deda 100644 Binary files a/bin/OSS.dll and b/bin/OSS.dll differ diff --git a/bin/OSS.pdb b/bin/OSS.pdb index c20489b..4b99dac 100644 Binary files a/bin/OSS.pdb and b/bin/OSS.pdb differ diff --git a/obj/Debug/DesignTimeResolveAssemblyReferences.cache b/obj/Debug/DesignTimeResolveAssemblyReferences.cache index c40433e..2f7b85b 100644 Binary files a/obj/Debug/DesignTimeResolveAssemblyReferences.cache and b/obj/Debug/DesignTimeResolveAssemblyReferences.cache differ diff --git a/obj/Debug/OSS.csproj.AssemblyReference.cache b/obj/Debug/OSS.csproj.AssemblyReference.cache index 33280fc..f5e894a 100644 Binary files a/obj/Debug/OSS.csproj.AssemblyReference.cache and b/obj/Debug/OSS.csproj.AssemblyReference.cache differ diff --git a/obj/Debug/OSS.dll b/obj/Debug/OSS.dll index 4702952..ec2deda 100644 Binary files a/obj/Debug/OSS.dll and b/obj/Debug/OSS.dll differ diff --git a/obj/Debug/OSS.pdb b/obj/Debug/OSS.pdb index c20489b..4b99dac 100644 Binary files a/obj/Debug/OSS.pdb and b/obj/Debug/OSS.pdb differ