From dbb096dce9a80fbf6605711dbf64eb75324caf32 Mon Sep 17 00:00:00 2001 From: VishwaJaya01 Date: Sun, 20 Jul 2025 13:33:39 +0530 Subject: [PATCH 1/5] Add assignedToUserId parameter to LAMasterfile methods for user-specific filtering --- Controllers/LAMasterfileController.cs | 16 ++++++------ Data/DBInitializer.cs | 2 +- repositories/ILAMasterfileRepository.cs | 8 +++--- repositories/LAMasterfileRepository.cs | 34 +++++++++++++++---------- services/ILAMasterfileService.cs | 8 +++--- services/LAMasterfileService.cs | 17 ++++++------- 6 files changed, 46 insertions(+), 39 deletions(-) diff --git a/Controllers/LAMasterfileController.cs b/Controllers/LAMasterfileController.cs index 0636bd6..e6fa967 100644 --- a/Controllers/LAMasterfileController.cs +++ b/Controllers/LAMasterfileController.cs @@ -16,33 +16,33 @@ public LAMasterfileController(ILAMasterfileService service) } [HttpGet] - public ActionResult GetAll([FromQuery] int pageNumber = 0, [FromQuery] int pageSize = 15, [FromQuery] string sortBy = "") + public ActionResult GetAll([FromQuery] int pageNumber = 0, [FromQuery] int pageSize = 15, [FromQuery] string sortBy = "", [FromQuery] int? assignedToUserId = null) { // Adjust pageNumber to be 1-based for internal logic var page = pageNumber + 1; - return Ok(_service.GetPaged(page, pageSize, sortBy)); + return Ok(_service.GetPaged(page, pageSize, sortBy, assignedToUserId)); } [HttpGet("paged")] - public ActionResult GetPaged([FromQuery] int page = 1, [FromQuery] int pageSize = 10, [FromQuery] string sortBy = "") + public ActionResult GetPaged([FromQuery] int page = 1, [FromQuery] int pageSize = 10, [FromQuery] string sortBy = "", [FromQuery] int? assignedToUserId = null) { - return Ok(_service.GetPaged(page, pageSize, sortBy)); + return Ok(_service.GetPaged(page, pageSize, sortBy, assignedToUserId)); } [HttpPost("search")] [HttpPost("filter")] - public ActionResult Search([FromBody] LAQueryRequest request, [FromQuery] string sortBy = "") + public ActionResult Search([FromBody] LAQueryRequest request, [FromQuery] string sortBy = "", [FromQuery] int? assignedToUserId = null) { var query = request.Query.ToLower(); - var response = _service.Search(query, sortBy); + var response = _service.Search(query, sortBy, assignedToUserId); return Ok(response); } [HttpPost("search/paged")] - public ActionResult SearchPaged([FromBody] LAQueryRequest request, [FromQuery] int page = 1, [FromQuery] int pageSize = 10, [FromQuery] string sortBy = "") + public ActionResult SearchPaged([FromBody] LAQueryRequest request, [FromQuery] int page = 1, [FromQuery] int pageSize = 10, [FromQuery] string sortBy = "", [FromQuery] int? assignedToUserId = null) { var query = request.Query.ToLower(); - var response = _service.SearchPaged(query, page, pageSize, sortBy); + var response = _service.SearchPaged(query, page, pageSize, sortBy, assignedToUserId); return Ok(response); } } diff --git a/Data/DBInitializer.cs b/Data/DBInitializer.cs index 2099030..5716f1d 100644 --- a/Data/DBInitializer.cs +++ b/Data/DBInitializer.cs @@ -32,7 +32,7 @@ public static void Initialize(AppDbContext context) UpdateUserTaskUserIds(context); // Remove UserTasks that are not LM (Land Miscellaneous) - RemoveNonLMUserTasks(context); + // RemoveNonLMUserTasks(context); // Initialize Master Data InitializeMasterData(context); // Initialize Land Aquisition Master Files diff --git a/repositories/ILAMasterfileRepository.cs b/repositories/ILAMasterfileRepository.cs index e6299e4..24d2b8a 100644 --- a/repositories/ILAMasterfileRepository.cs +++ b/repositories/ILAMasterfileRepository.cs @@ -5,9 +5,9 @@ namespace ValuationBackend.Repositories { public interface ILAMasterfileRepository { - List GetAll(string sortBy = ""); - (List Items, int TotalCount) GetPaged(int page, int pageSize, string sortBy = ""); - List Search(string query, string sortBy = ""); - (List Items, int TotalCount) SearchPaged(string query, int page, int pageSize, string sortBy = ""); + List GetAll(string sortBy = "", int? assignedToUserId = null); + (List Items, int TotalCount) GetPaged(int page, int pageSize, string sortBy = "", int? assignedToUserId = null); + List Search(string query, string sortBy = "", int? assignedToUserId = null); + (List Items, int TotalCount) SearchPaged(string query, int page, int pageSize, string sortBy = "", int? assignedToUserId = null); } } diff --git a/repositories/LAMasterfileRepository.cs b/repositories/LAMasterfileRepository.cs index 87378c9..5e629d7 100644 --- a/repositories/LAMasterfileRepository.cs +++ b/repositories/LAMasterfileRepository.cs @@ -13,20 +13,19 @@ public LAMasterfileRepository(AppDbContext context) _context = context; } - public List GetAll(string sortBy = "") + public List GetAll(string sortBy = "", int? assignedToUserId = null) { var query = _context.LandAquisitionMasterFiles.AsQueryable(); + query = ApplyUserFiltering(query, assignedToUserId); query = ApplySorting(query, sortBy); return query.ToList(); } - public (List Items, int TotalCount) GetPaged(int page, int pageSize, string sortBy = "") + public (List Items, int TotalCount) GetPaged(int page, int pageSize, string sortBy = "", int? assignedToUserId = null) { var query = _context.LandAquisitionMasterFiles.AsQueryable(); - - // Apply sorting + query = ApplyUserFiltering(query, assignedToUserId); query = ApplySorting(query, sortBy); - var totalCount = query.Count(); var items = query.Skip((page - 1) * pageSize) .Take(pageSize) @@ -34,7 +33,7 @@ public List GetAll(string sortBy = "") return (items, totalCount); } - public List Search(string query, string sortBy = "") + public List Search(string query, string sortBy = "", int? assignedToUserId = null) { query = query.ToLower(); var baseQuery = _context.LandAquisitionMasterFiles.Where(f => @@ -44,14 +43,12 @@ public List Search(string query, string sortBy = "") || f.RequestingAuthorityReferenceNo.ToLower().Contains(query) || f.Status.ToLower().Contains(query) ); - - // Apply sorting + baseQuery = ApplyUserFiltering(baseQuery, assignedToUserId); baseQuery = ApplySorting(baseQuery, sortBy); - return baseQuery.ToList(); } - public (List Items, int TotalCount) SearchPaged(string query, int page, int pageSize, string sortBy = "") + public (List Items, int TotalCount) SearchPaged(string query, int page, int pageSize, string sortBy = "", int? assignedToUserId = null) { query = query.ToLower(); var baseQuery = _context.LandAquisitionMasterFiles @@ -62,10 +59,8 @@ public List Search(string query, string sortBy = "") f.PlanType.ToLower().Contains(query) || f.RequestingAuthorityReferenceNo.ToLower().Contains(query) || f.Status.ToLower().Contains(query)); - - // Apply sorting + baseQuery = ApplyUserFiltering(baseQuery, assignedToUserId); baseQuery = ApplySorting(baseQuery, sortBy); - var totalCount = baseQuery.Count(); var items = baseQuery.Skip((page - 1) * pageSize) .Take(pageSize) @@ -91,5 +86,18 @@ private IQueryable ApplySorting(IQueryable query.OrderBy(x => x.Id) // Default fallback }; } + + private IQueryable ApplyUserFiltering(IQueryable query, int? assignedToUserId) + { + if (!assignedToUserId.HasValue) + { + return query; // No filtering if no user specified + } + // Join with UserTasks to filter by assigned user ID for LA tasks + return query.Where(la => _context.UserTasks + .Any(ut => ut.UserId == assignedToUserId.Value && + ut.TaskType == "LA" && + ut.LandAcquisitionId == la.Id)); + } } } diff --git a/services/ILAMasterfileService.cs b/services/ILAMasterfileService.cs index bd60287..b5584cc 100644 --- a/services/ILAMasterfileService.cs +++ b/services/ILAMasterfileService.cs @@ -4,9 +4,9 @@ namespace ValuationBackend.Services { public interface ILAMasterfileService { - LAMasterfileResponse GetAll(string sortBy = ""); - LAMasterfileResponse GetPaged(int page, int pageSize, string sortBy = ""); - LAMasterfileResponse Search(string query, string sortBy = ""); - LAMasterfileResponse SearchPaged(string query, int page, int pageSize, string sortBy = ""); + LAMasterfileResponse GetAll(string sortBy = "", int? assignedToUserId = null); + LAMasterfileResponse GetPaged(int page, int pageSize, string sortBy = "", int? assignedToUserId = null); + LAMasterfileResponse Search(string query, string sortBy = "", int? assignedToUserId = null); + LAMasterfileResponse SearchPaged(string query, int page, int pageSize, string sortBy = "", int? assignedToUserId = null); } } diff --git a/services/LAMasterfileService.cs b/services/LAMasterfileService.cs index 19b4224..6968d68 100644 --- a/services/LAMasterfileService.cs +++ b/services/LAMasterfileService.cs @@ -13,18 +13,18 @@ public LAMasterfileService(ILAMasterfileRepository repository) _repository = repository; } - public LAMasterfileResponse GetAll(string sortBy = "") + public LAMasterfileResponse GetAll(string sortBy = "", int? assignedToUserId = null) { - var data = _repository.GetAll(sortBy); + var data = _repository.GetAll(sortBy, assignedToUserId); return new LAMasterfileResponse { MasterFiles = data }; } - public LAMasterfileResponse GetPaged(int page, int pageSize, string sortBy = "") + public LAMasterfileResponse GetPaged(int page, int pageSize, string sortBy = "", int? assignedToUserId = null) { if (page < 1) page = 1; if (pageSize < 1) pageSize = 10; - var (items, totalCount) = _repository.GetPaged(page, pageSize, sortBy); + var (items, totalCount) = _repository.GetPaged(page, pageSize, sortBy, assignedToUserId); var totalPages = (int)Math.Ceiling(totalCount / (double)pageSize); return new LAMasterfileResponse @@ -38,18 +38,18 @@ public LAMasterfileResponse GetPaged(int page, int pageSize, string sortBy = "") }; } - public LAMasterfileResponse Search(string query, string sortBy = "") + public LAMasterfileResponse Search(string query, string sortBy = "", int? assignedToUserId = null) { - var data = _repository.Search(query, sortBy); + var data = _repository.Search(query, sortBy, assignedToUserId); return new LAMasterfileResponse { MasterFiles = data }; } - public LAMasterfileResponse SearchPaged(string query, int page, int pageSize, string sortBy = "") + public LAMasterfileResponse SearchPaged(string query, int page, int pageSize, string sortBy = "", int? assignedToUserId = null) { if (page < 1) page = 1; if (pageSize < 1) pageSize = 10; - var (items, totalCount) = _repository.SearchPaged(query, page, pageSize, sortBy); + var (items, totalCount) = _repository.SearchPaged(query, page, pageSize, sortBy, assignedToUserId); var totalPages = (int)Math.Ceiling(totalCount / (double)pageSize); return new LAMasterfileResponse @@ -59,7 +59,6 @@ public LAMasterfileResponse SearchPaged(string query, int page, int pageSize, st CurrentPage = page, PageSize = pageSize, TotalPages = totalPages, - SearchTerm = query, SortBy = sortBy }; } From e1d408dd7c5f9e71789f13b69aeb8798d592612f Mon Sep 17 00:00:00 2001 From: rithakith Date: Sun, 20 Jul 2025 16:42:12 +0530 Subject: [PATCH 2/5] deploy.yml create --- .github/workflows/deploy.yml | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) create mode 100644 .github/workflows/deploy.yml diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml new file mode 100644 index 0000000..00951a5 --- /dev/null +++ b/.github/workflows/deploy.yml @@ -0,0 +1,21 @@ +name: Deploy to EC2 +on: + push: + branches: [main] + +jobs: + deploy: + runs-on: ubuntu-latest + steps: + - name: SSH and deploy + uses: appleboy/ssh-action@master + with: + host: ${{ secrets.EC2_HOST }} + username: ${{ secrets.EC2_USER }} + key: ${{ secrets.EC2_KEY }} + script: | + cd /home/ec2-user/ValuationBackend + git pull + dotnet restore + sudo dotnet publish -c Release -o /var/app + sudo systemctl restart valuation-backend From 8170fee54c7fa42b367dc58daf83cec8e280926f Mon Sep 17 00:00:00 2001 From: VishwaJaya01 Date: Sun, 20 Jul 2025 19:19:50 +0530 Subject: [PATCH 3/5] Add ProfilePicture field to User model and update migrations - Added a ProfilePicture property of type byte[] to the User model. - Created a new migration to add the ProfilePicture column to the Users table. - Updated the AppDbContextModelSnapshot to reflect the new ProfilePicture field. - Modified LoginModels to include ProfilePicture in the Login response. --- Controllers/ProfileController.cs | 3 +- Controllers/UserProfileController.cs | 43 + ...082954_AddProfilePictureToUser.Designer.cs | 1713 +++++++++++++++++ .../20250720082954_AddProfilePictureToUser.cs | 22 + Migrations/AppDbContextModelSnapshot.cs | 8 +- Models/LoginModels.cs | 2 + 6 files changed, 1787 insertions(+), 4 deletions(-) create mode 100644 Controllers/UserProfileController.cs create mode 100644 Migrations/20250720082954_AddProfilePictureToUser.Designer.cs create mode 100644 Migrations/20250720082954_AddProfilePictureToUser.cs diff --git a/Controllers/ProfileController.cs b/Controllers/ProfileController.cs index 4e9877f..384ddd1 100644 --- a/Controllers/ProfileController.cs +++ b/Controllers/ProfileController.cs @@ -28,7 +28,8 @@ public IActionResult GetUserProfile([FromBody] UserProfileRequest request) empEmail = user.EmpEmail, empId = user.EmpId, position = user.Position, - assignedDivision = user.AssignedDivision + assignedDivision = user.AssignedDivision, + profilePicture = user.ProfilePicture != null ? Convert.ToBase64String(user.ProfilePicture) : null }); } } diff --git a/Controllers/UserProfileController.cs b/Controllers/UserProfileController.cs new file mode 100644 index 0000000..e3766fe --- /dev/null +++ b/Controllers/UserProfileController.cs @@ -0,0 +1,43 @@ +using Microsoft.AspNetCore.Mvc; +using Microsoft.EntityFrameworkCore; +using ValuationBackend.Data; + +namespace ValuationBackend.Controllers +{ + public class ProfilePictureUploadDto + { + public int UserId { get; set; } + public IFormFile Image { get; set; } + } + + [ApiController] + [Route("api/[controller]")] + public class UserProfileController : ControllerBase + { + private readonly AppDbContext _context; + + public UserProfileController(AppDbContext context) + { + _context = context; + } + + [HttpPost("upload-profile-picture")] + public async Task UploadProfilePicture([FromForm] ProfilePictureUploadDto dto) + { + if (dto.Image == null || dto.Image.Length == 0) + return BadRequest("No image uploaded."); + + var user = await _context.Users.FirstOrDefaultAsync(u => u.Id == dto.UserId); + if (user == null) + return NotFound("User not found."); + + using var ms = new MemoryStream(); + await dto.Image.CopyToAsync(ms); + user.ProfilePicture = ms.ToArray(); + + await _context.SaveChangesAsync(); + + return Ok("Profile picture updated."); + } + } +} \ No newline at end of file diff --git a/Migrations/20250720082954_AddProfilePictureToUser.Designer.cs b/Migrations/20250720082954_AddProfilePictureToUser.Designer.cs new file mode 100644 index 0000000..41010fa --- /dev/null +++ b/Migrations/20250720082954_AddProfilePictureToUser.Designer.cs @@ -0,0 +1,1713 @@ +// +using System; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; +using ValuationBackend.Data; + +#nullable disable + +namespace ValuationBackend.Migrations +{ + [DbContext(typeof(AppDbContext))] + [Migration("20250720082954_AddProfilePictureToUser")] + partial class AddProfilePictureToUser + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "9.0.5") + .HasAnnotation("Relational:MaxIdentifierLength", 63); + + NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); + + modelBuilder.Entity("LandAquisitionMasterFile", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("MasterFileNo") + .HasColumnType("integer"); + + b.Property("MasterFilesRefNo") + .IsRequired() + .HasColumnType("text"); + + b.Property("PlanNo") + .IsRequired() + .HasColumnType("text"); + + b.Property("PlanType") + .IsRequired() + .HasColumnType("text"); + + b.Property("RequestingAuthorityReferenceNo") + .IsRequired() + .HasColumnType("text"); + + b.Property("Status") + .IsRequired() + .HasColumnType("text"); + + b.HasKey("Id"); + + b.ToTable("LandAquisitionMasterFiles"); + }); + + modelBuilder.Entity("ValuationBackend.Models.Asset", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("AssetNo") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("Description") + .HasColumnType("integer"); + + b.Property("IsRatingCard") + .HasColumnType("boolean"); + + b.Property("Owner") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.Property("RdSt") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.Property("RequestId") + .HasColumnType("integer"); + + b.Property("UpdatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("Ward") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.HasKey("Id"); + + b.HasIndex("RequestId"); + + b.ToTable("Assets"); + }); + + modelBuilder.Entity("ValuationBackend.Models.AssetDivision", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("Area") + .HasColumnType("numeric"); + + b.Property("AssetId") + .HasColumnType("integer"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("Description") + .IsRequired() + .HasMaxLength(500) + .HasColumnType("character varying(500)"); + + b.Property("LandType") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("NewAssetNo") + .IsRequired() + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("AssetId"); + + b.ToTable("AssetDivisions"); + }); + + modelBuilder.Entity("ValuationBackend.Models.AssetNumberChange", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("ChangedDate") + .HasColumnType("timestamp with time zone"); + + b.Property("DateOfChange") + .HasColumnType("timestamp with time zone"); + + b.Property("Description") + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + b.Property("FieldSize") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("FieldType") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("NewAssetNo") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("OldAssetNo") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("Reason") + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.HasKey("Id"); + + b.ToTable("AssetNumberChanges"); + }); + + modelBuilder.Entity("ValuationBackend.Models.BuildingRatesLA", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("AssessmentNumber") + .HasColumnType("text"); + + b.Property("ConstructedBy") + .HasColumnType("text"); + + b.Property("Cost") + .HasColumnType("text"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("DescriptionOfProperty") + .HasColumnType("text"); + + b.Property("FloorAreaSQFT") + .HasColumnType("text"); + + b.Property("LocationLatitude") + .HasColumnType("text"); + + b.Property("LocationLongitude") + .HasColumnType("text"); + + b.Property("MasterFileId") + .IsRequired() + .HasColumnType("text"); + + b.Property("Owner") + .HasColumnType("text"); + + b.Property("RatePerSQFT") + .HasColumnType("text"); + + b.Property("Remarks") + .HasColumnType("text"); + + b.Property("ReportId") + .HasColumnType("integer"); + + b.Property("UpdatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("YearOfConstruction") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("ReportId"); + + b.ToTable("BuildingRatesLA"); + }); + + modelBuilder.Entity("ValuationBackend.Models.ConditionReport", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("AccessCategory") + .IsRequired() + .HasColumnType("text"); + + b.Property("AccessCategoryDescription") + .IsRequired() + .HasColumnType("text"); + + b.Property("AcquiredExtent") + .IsRequired() + .HasColumnType("text"); + + b.Property("AcquiringOfficerSignature") + .IsRequired() + .HasColumnType("text"); + + b.Property("AcquisitionName") + .IsRequired() + .HasColumnType("text"); + + b.Property("AssessmentNumber") + .IsRequired() + .HasColumnType("text"); + + b.Property("AtLotNumber") + .IsRequired() + .HasColumnType("text"); + + b.Property("AtPlanNumber") + .IsRequired() + .HasColumnType("text"); + + b.Property("BoundaryBottom") + .IsRequired() + .HasColumnType("text"); + + b.Property("BoundaryEast") + .IsRequired() + .HasColumnType("text"); + + b.Property("BoundaryNorth") + .IsRequired() + .HasColumnType("text"); + + b.Property("BoundarySouth") + .IsRequired() + .HasColumnType("text"); + + b.Property("BoundaryWest") + .IsRequired() + .HasColumnType("text"); + + b.Property("BuildingDescription") + .IsRequired() + .HasColumnType("text"); + + b.Property("BuildingInfo") + .IsRequired() + .HasColumnType("text"); + + b.Property("ChiefValuerRepresentativeSignature") + .IsRequired() + .HasColumnType("text"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("DateOfSection3BA") + .IsRequired() + .HasColumnType("text"); + + b.Property("DatePrepared") + .IsRequired() + .HasColumnType("text"); + + b.Property("DepthOfLand") + .IsRequired() + .HasColumnType("text"); + + b.Property("DescriptionOfLand") + .IsRequired() + .HasColumnType("text"); + + b.Property("DetailsOfBusiness") + .IsRequired() + .HasColumnType("text"); + + b.Property("Frontage") + .IsRequired() + .HasColumnType("text"); + + b.Property("GramasewakaSignature") + .IsRequired() + .HasColumnType("text"); + + b.Property("LandUseDescription") + .IsRequired() + .HasColumnType("text"); + + b.Property("LandUseType") + .IsRequired() + .HasColumnType("text"); + + b.Property("LevelWithAccess") + .IsRequired() + .HasColumnType("text"); + + b.Property("MasterFileId") + .IsRequired() + .HasColumnType("text"); + + b.Property("NameOfTheLand") + .IsRequired() + .HasColumnType("text"); + + b.Property("NameOfTheVillage") + .IsRequired() + .HasColumnType("text"); + + b.Property("OtherConstructionsDescription") + .IsRequired() + .HasColumnType("text"); + + b.Property("OtherConstructionsInfo") + .IsRequired() + .HasColumnType("text"); + + b.Property("PlantationDetails") + .IsRequired() + .HasColumnType("text"); + + b.Property("PpCadLotNumber") + .IsRequired() + .HasColumnType("text"); + + b.Property("PpCadNumber") + .IsRequired() + .HasColumnType("text"); + + b.Property("ReportId") + .HasColumnType("integer"); + + b.Property("RoadName") + .IsRequired() + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("ReportId"); + + b.ToTable("ConditionReports"); + }); + + modelBuilder.Entity("ValuationBackend.Models.DomesticRatingCard", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("Access") + .HasColumnType("text"); + + b.Property("Age") + .HasColumnType("integer"); + + b.Property("AssetId") + .HasColumnType("integer"); + + b.Property("Condition") + .HasColumnType("text"); + + b.Property("Conveniences") + .HasColumnType("text"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("Date") + .HasColumnType("timestamp with time zone"); + + b.Property("Description") + .IsRequired() + .HasColumnType("text"); + + b.Property("Floor") + .HasColumnType("text"); + + b.Property("NewNumber") + .IsRequired() + .HasColumnType("text"); + + b.Property("Notes") + .HasColumnType("text"); + + b.Property("Occupier") + .HasColumnType("text"); + + b.Property("Owner") + .IsRequired() + .HasColumnType("text"); + + b.Property("ParkingSpace") + .HasColumnType("text"); + + b.Property("Plantations") + .HasColumnType("text"); + + b.Property("PropertySubCategory") + .HasColumnType("text"); + + b.Property("PropertyType") + .HasColumnType("text"); + + b.Property("RentPM") + .HasColumnType("numeric"); + + b.Property("RoadName") + .HasColumnType("text"); + + b.Property("SelectWalls") + .HasColumnType("text"); + + b.Property("SuggestedRate") + .HasColumnType("numeric"); + + b.Property("Terms") + .HasColumnType("text"); + + b.Property("TsBop") + .HasColumnType("text"); + + b.Property("WardNumber") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("AssetId"); + + b.ToTable("DomesticRatingCards"); + }); + + modelBuilder.Entity("ValuationBackend.Models.ImageData", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("ImageBase64") + .IsRequired() + .HasColumnType("text"); + + b.Property("ReportId") + .HasColumnType("integer"); + + b.HasKey("Id"); + + b.ToTable("ImageData"); + }); + + modelBuilder.Entity("ValuationBackend.Models.InspectionBuilding", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("AgeYears") + .HasColumnType("text"); + + b.Property("BathroomToilet") + .HasColumnType("text"); + + b.Property("BathroomToiletDoorsFittings") + .HasColumnType("text"); + + b.Property("BuildingCategory") + .HasColumnType("text"); + + b.Property("BuildingClass") + .HasColumnType("text"); + + b.Property("BuildingConditions") + .HasColumnType("text"); + + b.Property("BuildingId") + .HasColumnType("text"); + + b.Property("BuildingName") + .HasColumnType("text"); + + b.Property("Ceiling") + .HasColumnType("text"); + + b.Property("Condition") + .HasColumnType("text"); + + b.Property("Conveniences") + .HasColumnType("text"); + + b.Property("Design") + .HasColumnType("text"); + + b.Property("DetailOfBuilding") + .HasColumnType("text"); + + b.Property("Door") + .HasColumnType("text"); + + b.Property("ExpectedLifePeriodYears") + .HasColumnType("text"); + + b.Property("FloorFinisher") + .HasColumnType("text"); + + b.Property("FloorStructure") + .HasColumnType("text"); + + b.Property("FoundationStructure") + .HasColumnType("text"); + + b.Property("HandRail") + .HasColumnType("text"); + + b.Property("InspectionReportId") + .HasColumnType("integer"); + + b.Property("NatureOfConstruction") + .HasColumnType("text"); + + b.Property("NoOfFloorsAboveGround") + .HasColumnType("text") + .HasColumnName("NoOfFloorsAboveGround"); + + b.Property("NoOfFloorsBelowGround") + .HasColumnType("text") + .HasColumnName("NoOfFloorsBelowGround"); + + b.Property("OtherDoors") + .HasColumnType("text"); + + b.Property("PantryCupboard") + .HasColumnType("text"); + + b.Property("ParkingSpace") + .HasColumnType("text"); + + b.Property("RoofFinisher") + .HasColumnType("text"); + + b.Property("RoofFrame") + .HasColumnType("text"); + + b.Property("RoofMaterial") + .HasColumnType("text"); + + b.Property("Services") + .HasColumnType("text"); + + b.Property("Structure") + .HasColumnType("text"); + + b.Property("WallFinisher") + .HasColumnType("text"); + + b.Property("WallStructure") + .HasColumnType("text"); + + b.Property("Window") + .HasColumnType("text"); + + b.Property("WindowProtection") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("InspectionReportId"); + + b.ToTable("InspectionBuildings"); + }); + + modelBuilder.Entity("ValuationBackend.Models.InspectionReport", b => + { + b.Property("InspectionReportId") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("InspectionReportId")); + + b.Property("DetailsOfAssestsInventoryItems") + .HasColumnType("text") + .HasColumnName("DetailsOfAssestsInventoryItems"); + + b.Property("DetailsOfBusiness") + .HasColumnType("text"); + + b.Property("District") + .HasColumnType("text"); + + b.Property("DsDivision") + .HasColumnType("text"); + + b.Property("GnDivision") + .HasColumnType("text"); + + b.Property("InspectionDate") + .HasColumnType("timestamp with time zone"); + + b.Property("MasterFileId") + .IsRequired() + .HasColumnType("text"); + + b.Property("MasterFileRefNo") + .IsRequired() + .HasColumnType("text"); + + b.Property("OtherConstructionDetails") + .HasColumnType("text"); + + b.Property("OtherInformation") + .HasColumnType("text"); + + b.Property("Province") + .HasColumnType("text"); + + b.Property("Remark") + .HasColumnType("text"); + + b.Property("ReportId") + .HasColumnType("integer"); + + b.Property("Village") + .HasColumnType("text"); + + b.HasKey("InspectionReportId"); + + b.HasIndex("ReportId"); + + b.ToTable("InspectionReports"); + }); + + modelBuilder.Entity("ValuationBackend.Models.LMBuildingRates", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("AssessmentNumber") + .HasColumnType("text"); + + b.Property("ConstructedBy") + .HasColumnType("text"); + + b.Property("Cost") + .HasColumnType("text"); + + b.Property("DescriptionOfProperty") + .HasColumnType("text"); + + b.Property("FloorArea") + .HasColumnType("text"); + + b.Property("LocationLatitude") + .HasColumnType("text"); + + b.Property("LocationLongitude") + .HasColumnType("text"); + + b.Property("MasterFileRefNo") + .IsRequired() + .HasColumnType("text"); + + b.Property("Owner") + .HasColumnType("text"); + + b.Property("RatePerSQFT") + .HasColumnType("text"); + + b.Property("Remarks") + .HasColumnType("text"); + + b.Property("ReportId") + .HasColumnType("integer"); + + b.Property("YearOfConstruction") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("ReportId"); + + b.ToTable("LMBuildingRates"); + }); + + modelBuilder.Entity("ValuationBackend.Models.LMPastValuation", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("DateOfValuation") + .HasColumnType("text"); + + b.Property("Extent") + .HasColumnType("text"); + + b.Property("FileNo_GnDivision") + .HasColumnType("text"); + + b.Property("LocationLatitude") + .HasColumnType("text"); + + b.Property("LocationLongitude") + .HasColumnType("text"); + + b.Property("MasterFileRefNo") + .IsRequired() + .HasColumnType("text"); + + b.Property("PlanOfParticulars") + .HasColumnType("text"); + + b.Property("PurposeOfValuation") + .HasColumnType("text"); + + b.Property("Rate") + .HasColumnType("text"); + + b.Property("RateType") + .HasColumnType("text"); + + b.Property("Remarks") + .HasColumnType("text"); + + b.Property("ReportId") + .HasColumnType("integer"); + + b.Property("Situation") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("ReportId"); + + b.ToTable("LMPastValuations"); + }); + + modelBuilder.Entity("ValuationBackend.Models.LMRentalEvidence", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("AssessmentNo") + .HasColumnType("text"); + + b.Property("Description") + .HasColumnType("text"); + + b.Property("FloorRate") + .HasColumnType("text"); + + b.Property("HeadOfTerms") + .HasColumnType("text"); + + b.Property("LocationLatitude") + .HasColumnType("text"); + + b.Property("LocationLongitude") + .HasColumnType("text"); + + b.Property("MasterFileRefNo") + .IsRequired() + .HasColumnType("text"); + + b.Property("Occupier") + .HasColumnType("text"); + + b.Property("Owner") + .HasColumnType("text"); + + b.Property("RatePer") + .HasColumnType("text"); + + b.Property("RatePerMonth") + .HasColumnType("text"); + + b.Property("Remarks") + .HasColumnType("text"); + + b.Property("ReportId") + .HasColumnType("integer"); + + b.Property("Situation") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("ReportId"); + + b.ToTable("LMRentalEvidences"); + }); + + modelBuilder.Entity("ValuationBackend.Models.LMSalesEvidence", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("AssetNumber") + .HasColumnType("text"); + + b.Property("Consideration") + .HasColumnType("text"); + + b.Property("DeedAttestedNumber") + .HasColumnType("text"); + + b.Property("DeedNumber") + .HasColumnType("text"); + + b.Property("DescriptionOfProperty") + .HasColumnType("text"); + + b.Property("Extent") + .HasColumnType("text"); + + b.Property("LandRegistryReferences") + .HasColumnType("text"); + + b.Property("LocationLatitude") + .HasColumnType("text"); + + b.Property("LocationLongitude") + .HasColumnType("text"); + + b.Property("LotNumber") + .HasColumnType("text"); + + b.Property("MasterFileRefNo") + .IsRequired() + .HasColumnType("text"); + + b.Property("NotaryName") + .HasColumnType("text"); + + b.Property("PlanDate") + .HasColumnType("text"); + + b.Property("PlanNumber") + .HasColumnType("text"); + + b.Property("Rate") + .HasColumnType("text"); + + b.Property("RateType") + .HasColumnType("text"); + + b.Property("Remarks") + .HasColumnType("text"); + + b.Property("ReportId") + .HasColumnType("integer"); + + b.Property("Road") + .HasColumnType("text"); + + b.Property("Situation") + .HasColumnType("text"); + + b.Property("Vendor") + .HasColumnType("text"); + + b.Property("Village") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("ReportId"); + + b.ToTable("LMSalesEvidences"); + }); + + modelBuilder.Entity("ValuationBackend.Models.LandMiscellaneousMasterFile", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("Lots") + .HasColumnType("integer"); + + b.Property("MasterFileNo") + .HasColumnType("integer"); + + b.Property("PlanNo") + .HasColumnType("text"); + + b.Property("PlanType") + .HasColumnType("text"); + + b.Property("RequestingAuthorityReferenceNo") + .HasColumnType("text"); + + b.Property("Status") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.ToTable("LandMiscellaneousMasterFiles"); + }); + + modelBuilder.Entity("ValuationBackend.Models.MasterDataItem", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("Category") + .IsRequired() + .HasColumnType("text"); + + b.Property("Value") + .IsRequired() + .HasColumnType("text"); + + b.HasKey("Id"); + + b.ToTable("MasterDataItems"); + }); + + modelBuilder.Entity("ValuationBackend.Models.PastValuationsLA", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("DateOfValuation") + .HasColumnType("text"); + + b.Property("Extent") + .HasColumnType("text"); + + b.Property("FileNoGNDivision") + .HasColumnType("text"); + + b.Property("LocationLatitude") + .HasColumnType("text"); + + b.Property("LocationLongitude") + .HasColumnType("text"); + + b.Property("MasterFileId") + .IsRequired() + .HasColumnType("text"); + + b.Property("MasterFileRefNo") + .IsRequired() + .HasColumnType("text"); + + b.Property("PlanOfParticulars") + .HasColumnType("text"); + + b.Property("PurposeOfValuation") + .HasColumnType("text"); + + b.Property("Rate") + .HasColumnType("text"); + + b.Property("RateType") + .HasColumnType("text"); + + b.Property("Remarks") + .HasColumnType("text"); + + b.Property("ReportId") + .HasColumnType("integer"); + + b.Property("Situation") + .HasColumnType("text"); + + b.Property("UpdatedAt") + .HasColumnType("timestamp with time zone"); + + b.HasKey("Id"); + + b.HasIndex("ReportId"); + + b.ToTable("PastValuationsLA"); + }); + + modelBuilder.Entity("ValuationBackend.Models.PropertyCategory", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("Name") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.HasKey("Id"); + + b.ToTable("PropertyCategories"); + }); + + modelBuilder.Entity("ValuationBackend.Models.RatingRequest", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("LocalAuthority") + .IsRequired() + .HasColumnType("text"); + + b.Property("RatingReferenceNo") + .IsRequired() + .HasColumnType("text"); + + b.Property("RequestType") + .IsRequired() + .HasColumnType("text"); + + b.Property("Status") + .IsRequired() + .HasColumnType("text"); + + b.Property("YearOfRevision") + .HasColumnType("integer"); + + b.HasKey("Id"); + + b.ToTable("RatingRequests"); + }); + + modelBuilder.Entity("ValuationBackend.Models.Reconciliation", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("AssetId") + .HasColumnType("integer"); + + b.Property("NewNo") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("ObsoleteNo") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("StreetName") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.Property("UpdatedAt") + .HasColumnType("timestamp with time zone"); + + b.HasKey("Id"); + + b.HasIndex("AssetId"); + + b.ToTable("Reconciliations"); + }); + + modelBuilder.Entity("ValuationBackend.Models.RentalEvidenceLA", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("AssessmentNo") + .HasColumnType("text"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("Description") + .HasColumnType("text"); + + b.Property("FloorRateSQFT") + .HasColumnType("text"); + + b.Property("HeadOfTerms") + .HasColumnType("text"); + + b.Property("LocationLatitude") + .HasColumnType("text"); + + b.Property("LocationLongitude") + .HasColumnType("text"); + + b.Property("MasterFileId") + .IsRequired() + .HasColumnType("text"); + + b.Property("MasterFileRefNo") + .IsRequired() + .HasColumnType("text"); + + b.Property("Occupier") + .HasColumnType("text"); + + b.Property("Owner") + .HasColumnType("text"); + + b.Property("RatePerMonth") + .HasColumnType("text"); + + b.Property("RatePerSqft") + .HasColumnType("text"); + + b.Property("Remarks") + .HasColumnType("text"); + + b.Property("ReportId") + .HasColumnType("integer"); + + b.Property("Situation") + .HasColumnType("text"); + + b.Property("UpdatedAt") + .HasColumnType("timestamp with time zone"); + + b.HasKey("Id"); + + b.HasIndex("ReportId"); + + b.ToTable("RentalEvidencesLA", (string)null); + }); + + modelBuilder.Entity("ValuationBackend.Models.Report", b => + { + b.Property("ReportId") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("ReportId")); + + b.Property("Description") + .IsRequired() + .HasColumnType("text"); + + b.Property("ReportType") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("Timestamp") + .HasColumnType("timestamp with time zone"); + + b.HasKey("ReportId"); + + b.ToTable("Reports"); + }); + + modelBuilder.Entity("ValuationBackend.Models.Request", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("LocalAuthority") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("RatingReferenceNo") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("RequestTypeId") + .HasColumnType("integer"); + + b.Property("Status") + .HasColumnType("boolean"); + + b.Property("UpdatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("YearOfRevision") + .HasColumnType("integer"); + + b.HasKey("Id"); + + b.HasIndex("RequestTypeId"); + + b.ToTable("Requests"); + }); + + modelBuilder.Entity("ValuationBackend.Models.RequestType", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("Code") + .IsRequired() + .HasMaxLength(2) + .HasColumnType("character varying(2)"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.HasKey("Id"); + + b.ToTable("RequestTypes"); + }); + + modelBuilder.Entity("ValuationBackend.Models.SalesEvidenceLA", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("AssetNumber") + .HasColumnType("text"); + + b.Property("Consideration") + .HasColumnType("text"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("DeedAttestedNumber") + .HasColumnType("text"); + + b.Property("DeedNumber") + .HasColumnType("text"); + + b.Property("Description") + .HasColumnType("text"); + + b.Property("DescriptionOfProperty") + .HasColumnType("text"); + + b.Property("Extent") + .HasColumnType("text"); + + b.Property("FloorRate") + .HasColumnType("text"); + + b.Property("LandRegistryReferences") + .HasColumnType("text"); + + b.Property("LocationLatitude") + .HasColumnType("text"); + + b.Property("LocationLongitude") + .HasColumnType("text"); + + b.Property("LotNumber") + .HasColumnType("text"); + + b.Property("MasterFileId") + .IsRequired() + .HasColumnType("text"); + + b.Property("MasterFileRefNo") + .IsRequired() + .HasColumnType("text"); + + b.Property("NotaryName") + .HasColumnType("text"); + + b.Property("Occupier") + .HasColumnType("text"); + + b.Property("Owner") + .HasColumnType("text"); + + b.Property("PlanDate") + .HasColumnType("text"); + + b.Property("PlanNumber") + .HasColumnType("text"); + + b.Property("Rate") + .HasColumnType("text"); + + b.Property("RateType") + .HasColumnType("text"); + + b.Property("Remarks") + .HasColumnType("text"); + + b.Property("ReportId") + .HasColumnType("integer"); + + b.Property("Road") + .HasColumnType("text"); + + b.Property("Situation") + .HasColumnType("text"); + + b.Property("UpdatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("Vendor") + .HasColumnType("text"); + + b.Property("Village") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("ReportId"); + + b.ToTable("SalesEvidencesLA", (string)null); + }); + + modelBuilder.Entity("ValuationBackend.Models.User", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("AssignedDivision") + .IsRequired() + .HasColumnType("text"); + + b.Property("EmpEmail") + .IsRequired() + .HasColumnType("text"); + + b.Property("EmpId") + .IsRequired() + .HasColumnType("text"); + + b.Property("EmpName") + .IsRequired() + .HasColumnType("text"); + + b.Property("PasswordHash") + .IsRequired() + .HasColumnType("bytea"); + + b.Property("PasswordSalt") + .IsRequired() + .HasColumnType("bytea"); + + b.Property("Position") + .IsRequired() + .HasColumnType("text"); + + b.Property("ProfilePicture") + .HasColumnType("bytea"); + + b.Property("Username") + .IsRequired() + .HasColumnType("text"); + + b.HasKey("Id"); + + b.ToTable("Users"); + }); + + modelBuilder.Entity("ValuationBackend.Models.UserTask", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("AssignedDate") + .HasColumnType("timestamp with time zone"); + + b.Property("IsCompleted") + .HasColumnType("boolean"); + + b.Property("LandAcquisitionId") + .HasColumnType("integer"); + + b.Property("LandMiscellaneousId") + .HasColumnType("integer"); + + b.Property("ReferenceNumber") + .HasColumnType("text"); + + b.Property("RequestId") + .HasColumnType("integer"); + + b.Property("TaskType") + .IsRequired() + .HasColumnType("text"); + + b.Property("UserId") + .HasColumnType("integer"); + + b.Property("Username") + .IsRequired() + .HasColumnType("text"); + + b.Property("WorkItemDescription") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("UserTasks"); + }); + + modelBuilder.Entity("ValuationBackend.Models.Asset", b => + { + b.HasOne("ValuationBackend.Models.Request", "Request") + .WithMany() + .HasForeignKey("RequestId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Request"); + }); + + modelBuilder.Entity("ValuationBackend.Models.AssetDivision", b => + { + b.HasOne("ValuationBackend.Models.Asset", "Asset") + .WithMany() + .HasForeignKey("AssetId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Asset"); + }); + + modelBuilder.Entity("ValuationBackend.Models.BuildingRatesLA", b => + { + b.HasOne("ValuationBackend.Models.Report", "Report") + .WithMany() + .HasForeignKey("ReportId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Report"); + }); + + modelBuilder.Entity("ValuationBackend.Models.ConditionReport", b => + { + b.HasOne("ValuationBackend.Models.Report", "Report") + .WithMany() + .HasForeignKey("ReportId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Report"); + }); + + modelBuilder.Entity("ValuationBackend.Models.DomesticRatingCard", b => + { + b.HasOne("ValuationBackend.Models.Asset", "Asset") + .WithMany() + .HasForeignKey("AssetId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Asset"); + }); + + modelBuilder.Entity("ValuationBackend.Models.InspectionBuilding", b => + { + b.HasOne("ValuationBackend.Models.InspectionReport", "InspectionReport") + .WithMany("Buildings") + .HasForeignKey("InspectionReportId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("InspectionReport"); + }); + + modelBuilder.Entity("ValuationBackend.Models.InspectionReport", b => + { + b.HasOne("ValuationBackend.Models.Report", "Report") + .WithMany() + .HasForeignKey("ReportId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Report"); + }); + + modelBuilder.Entity("ValuationBackend.Models.LMBuildingRates", b => + { + b.HasOne("ValuationBackend.Models.Report", "Report") + .WithMany() + .HasForeignKey("ReportId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Report"); + }); + + modelBuilder.Entity("ValuationBackend.Models.LMPastValuation", b => + { + b.HasOne("ValuationBackend.Models.Report", "Report") + .WithMany() + .HasForeignKey("ReportId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Report"); + }); + + modelBuilder.Entity("ValuationBackend.Models.LMRentalEvidence", b => + { + b.HasOne("ValuationBackend.Models.Report", "Report") + .WithMany() + .HasForeignKey("ReportId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Report"); + }); + + modelBuilder.Entity("ValuationBackend.Models.LMSalesEvidence", b => + { + b.HasOne("ValuationBackend.Models.Report", "Report") + .WithMany() + .HasForeignKey("ReportId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Report"); + }); + + modelBuilder.Entity("ValuationBackend.Models.PastValuationsLA", b => + { + b.HasOne("ValuationBackend.Models.Report", "Report") + .WithMany() + .HasForeignKey("ReportId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Report"); + }); + + modelBuilder.Entity("ValuationBackend.Models.Reconciliation", b => + { + b.HasOne("ValuationBackend.Models.Asset", "Asset") + .WithMany() + .HasForeignKey("AssetId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Asset"); + }); + + modelBuilder.Entity("ValuationBackend.Models.RentalEvidenceLA", b => + { + b.HasOne("ValuationBackend.Models.Report", "Report") + .WithMany() + .HasForeignKey("ReportId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Report"); + }); + + modelBuilder.Entity("ValuationBackend.Models.Request", b => + { + b.HasOne("ValuationBackend.Models.RequestType", "RequestType") + .WithMany() + .HasForeignKey("RequestTypeId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("RequestType"); + }); + + modelBuilder.Entity("ValuationBackend.Models.SalesEvidenceLA", b => + { + b.HasOne("ValuationBackend.Models.Report", "Report") + .WithMany() + .HasForeignKey("ReportId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Report"); + }); + + modelBuilder.Entity("ValuationBackend.Models.UserTask", b => + { + b.HasOne("ValuationBackend.Models.User", "User") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("User"); + }); + + modelBuilder.Entity("ValuationBackend.Models.InspectionReport", b => + { + b.Navigation("Buildings"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/Migrations/20250720082954_AddProfilePictureToUser.cs b/Migrations/20250720082954_AddProfilePictureToUser.cs new file mode 100644 index 0000000..ae9c3de --- /dev/null +++ b/Migrations/20250720082954_AddProfilePictureToUser.cs @@ -0,0 +1,22 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace ValuationBackend.Migrations +{ + /// + public partial class AddProfilePictureToUser : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + + } + } +} diff --git a/Migrations/AppDbContextModelSnapshot.cs b/Migrations/AppDbContextModelSnapshot.cs index fadf802..bf53bd9 100644 --- a/Migrations/AppDbContextModelSnapshot.cs +++ b/Migrations/AppDbContextModelSnapshot.cs @@ -515,9 +515,8 @@ protected override void BuildModel(ModelBuilder modelBuilder) .IsRequired() .HasColumnType("text"); - b.Property("ReportId") - .IsRequired() - .HasColumnType("text"); + b.Property("ReportId") + .HasColumnType("integer"); b.HasKey("Id"); @@ -1455,6 +1454,9 @@ protected override void BuildModel(ModelBuilder modelBuilder) .IsRequired() .HasColumnType("text"); + b.Property("ProfilePicture") + .HasColumnType("bytea"); + b.Property("Username") .IsRequired() .HasColumnType("text"); diff --git a/Models/LoginModels.cs b/Models/LoginModels.cs index c978556..7540db1 100644 --- a/Models/LoginModels.cs +++ b/Models/LoginModels.cs @@ -13,6 +13,8 @@ public class User public required string EmpId { get; set; } public required string Position { get; set; } public required string AssignedDivision { get; set; } + + public byte[]? ProfilePicture { get; set; } } public class LoginRequest From abf7138d045e5ca47b6d91499d901c2297475e3b Mon Sep 17 00:00:00 2001 From: VishwaJaya01 Date: Sun, 20 Jul 2025 22:53:35 +0530 Subject: [PATCH 4/5] Enhance UserTaskController to include detailed task type counts and improve logging for task overview --- Controllers/UserTaskController.cs | 18 ++++++++++++------ Models/UserTask.cs | 3 +++ 2 files changed, 15 insertions(+), 6 deletions(-) diff --git a/Controllers/UserTaskController.cs b/Controllers/UserTaskController.cs index 4cebd2a..95165a8 100644 --- a/Controllers/UserTaskController.cs +++ b/Controllers/UserTaskController.cs @@ -20,12 +20,18 @@ public ActionResult GetTaskOverview([FromBody] UserTas { var userTasks = _context.UserTasks.Where(t => t.Username == request.Username).ToList(); - var laAssigned = userTasks.Count(t => t.TaskType == "LA"); - var laCompleted = userTasks.Count(t => t.TaskType == "LA" && t.IsCompleted); - var mrAssigned = userTasks.Count(t => t.TaskType == "MR"); - var mrCompleted = userTasks.Count(t => t.TaskType == "MR" && t.IsCompleted); - var lmAssigned = userTasks.Count(t => t.TaskType == "LM"); - var lmCompleted = userTasks.Count(t => t.TaskType == "LM" && t.IsCompleted); + // Debug log: print all userTasks for this user + Console.WriteLine($"UserTasks for {request.Username}: {string.Join(", ", userTasks.Select(t => $"{t.TaskType}:{t.IsCompleted}"))}"); + + var laAssigned = userTasks.Count(t => t.TaskType != null && t.TaskType.ToUpper() == "LA"); + var laCompleted = userTasks.Count(t => t.TaskType != null && t.TaskType.ToUpper() == "LA" && t.IsCompleted); + var mrAssigned = userTasks.Count(t => t.TaskType != null && t.TaskType.ToUpper() == "MR"); + var mrCompleted = userTasks.Count(t => t.TaskType != null && t.TaskType.ToUpper() == "MR" && t.IsCompleted); + var lmAssigned = userTasks.Count(t => t.TaskType != null && t.TaskType.ToUpper() == "LM"); + var lmCompleted = userTasks.Count(t => t.TaskType != null && t.TaskType.ToUpper() == "LM" && t.IsCompleted); + + // Debug log: print computed counts + Console.WriteLine($"LA: {laAssigned}, MR: {mrAssigned}, LM: {lmAssigned}, LA Done: {laCompleted}, MR Done: {mrCompleted}, LM Done: {lmCompleted}"); return Ok(new UserTaskOverviewResponse { diff --git a/Models/UserTask.cs b/Models/UserTask.cs index f778c15..b7734aa 100644 --- a/Models/UserTask.cs +++ b/Models/UserTask.cs @@ -1,5 +1,8 @@ +using System.ComponentModel.DataAnnotations.Schema; + namespace ValuationBackend.Models { + [Table("UserTasks")] public class UserTask { public int Id { get; set; } From e26d9116e4c4883cd8a528bd810e42664124e003 Mon Sep 17 00:00:00 2001 From: rithakith Date: Mon, 21 Jul 2025 16:00:45 +0530 Subject: [PATCH 5/5] offices rating card api --- .../iteration2/OfficesRatingCardController.cs | 204 ++++++++++++++++++ Data/AppDbContext.cs | 3 + Extensions/RepositoryExtensions.cs | 3 + Extensions/ServiceExtensions.cs | 2 + Migrations/AddOfficesRatingCardTable.cs | 77 +++++++ .../iteration2/DTOs/OfficesRatingCardDto.cs | 121 +++++++++++ .../RatingCards/OfficesRatingCard.cs | 113 ++++++++++ Profiles/OfficesRatingCardProfile.cs | 38 ++++ .../IOfficesRatingCardRepository.cs | 18 ++ .../iteration2/OfficesRatingCardRepository.cs | 84 ++++++++ .../iteration2/IOfficesRatingCardService.cs | 17 ++ .../iteration2/OfficesRatingCardService.cs | 134 ++++++++++++ 12 files changed, 814 insertions(+) create mode 100644 Controllers/iteration2/OfficesRatingCardController.cs create mode 100644 Migrations/AddOfficesRatingCardTable.cs create mode 100644 Models/iteration2/DTOs/OfficesRatingCardDto.cs create mode 100644 Models/iteration2/RatingCards/OfficesRatingCard.cs create mode 100644 Profiles/OfficesRatingCardProfile.cs create mode 100644 repositories/iteration2/IOfficesRatingCardRepository.cs create mode 100644 repositories/iteration2/OfficesRatingCardRepository.cs create mode 100644 services/iteration2/IOfficesRatingCardService.cs create mode 100644 services/iteration2/OfficesRatingCardService.cs diff --git a/Controllers/iteration2/OfficesRatingCardController.cs b/Controllers/iteration2/OfficesRatingCardController.cs new file mode 100644 index 0000000..65aeb93 --- /dev/null +++ b/Controllers/iteration2/OfficesRatingCardController.cs @@ -0,0 +1,204 @@ +using System; +using System.Collections.Generic; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Mvc; +using Microsoft.Extensions.Logging; +using ValuationBackend.Models.iteration2.DTOs; +using ValuationBackend.services.iteration2; + +namespace ValuationBackend.Controllers.iteration2 +{ + [Route("api/[controller]")] + [ApiController] + public class OfficesRatingCardController : ControllerBase + { + private readonly IOfficesRatingCardService _service; + private readonly ILogger _logger; + + public OfficesRatingCardController( + IOfficesRatingCardService service, + ILogger logger) + { + _service = service; + _logger = logger; + } + + /// + /// Get all offices rating cards + /// + [HttpGet] + public async Task>> GetAll() + { + try + { + var result = await _service.GetAllAsync(); + return Ok(result); + } + catch (Exception ex) + { + _logger.LogError(ex, "Error retrieving all offices rating cards"); + return StatusCode(500, "An error occurred while retrieving offices rating cards."); + } + } + + /// + /// Get offices rating card by ID + /// + [HttpGet("{id}")] + public async Task> GetById(int id) + { + try + { + var result = await _service.GetByIdAsync(id); + return Ok(result); + } + catch (KeyNotFoundException) + { + return NotFound($"OfficesRatingCard with ID {id} not found."); + } + catch (Exception ex) + { + _logger.LogError(ex, "Error retrieving offices rating card with ID {Id}", id); + return StatusCode(500, "An error occurred while retrieving the offices rating card."); + } + } + + /// + /// Get offices rating card by Asset ID + /// + [HttpGet("asset/{assetId}")] + public async Task> GetByAssetId(int assetId) + { + try + { + var result = await _service.GetByAssetIdAsync(assetId); + return Ok(result); + } + catch (KeyNotFoundException) + { + return NotFound($"OfficesRatingCard for Asset ID {assetId} not found."); + } + catch (Exception ex) + { + _logger.LogError(ex, "Error retrieving offices rating card for Asset ID {AssetId}", assetId); + return StatusCode(500, "An error occurred while retrieving the offices rating card."); + } + } + + /// + /// Create a new offices rating card + /// + [HttpPost("asset/{assetId}")] + public async Task> Create(int assetId, [FromBody] CreateOfficesRatingCardDto dto) + { + try + { + if (dto == null) + { + return BadRequest("Invalid offices rating card data."); + } + + // Ensure the assetId from route matches the one in the body + dto.AssetId = assetId; + + if (!ModelState.IsValid) + { + return BadRequest(ModelState); + } + + var result = await _service.CreateAsync(dto); + return CreatedAtAction(nameof(GetById), new { id = result.Id }, result); + } + catch (InvalidOperationException ex) + { + return Conflict(ex.Message); + } + catch (KeyNotFoundException ex) + { + return NotFound(ex.Message); + } + catch (Exception ex) + { + _logger.LogError(ex, "Error creating offices rating card for Asset ID {AssetId}", assetId); + return StatusCode(500, "An error occurred while creating the offices rating card."); + } + } + + /// + /// Update an existing offices rating card + /// + [HttpPut("{id}")] + public async Task> Update(int id, [FromBody] UpdateOfficesRatingCardDto dto) + { + try + { + if (dto == null) + { + return BadRequest("Invalid offices rating card data."); + } + + if (!ModelState.IsValid) + { + return BadRequest(ModelState); + } + + var result = await _service.UpdateAsync(id, dto); + return Ok(result); + } + catch (KeyNotFoundException) + { + return NotFound($"OfficesRatingCard with ID {id} not found."); + } + catch (Exception ex) + { + _logger.LogError(ex, "Error updating offices rating card with ID {Id}", id); + return StatusCode(500, "An error occurred while updating the offices rating card."); + } + } + + /// + /// Delete an offices rating card + /// + [HttpDelete("{id}")] + public async Task Delete(int id) + { + try + { + var result = await _service.DeleteAsync(id); + if (!result) + { + return NotFound($"OfficesRatingCard with ID {id} not found."); + } + + return NoContent(); + } + catch (Exception ex) + { + _logger.LogError(ex, "Error deleting offices rating card with ID {Id}", id); + return StatusCode(500, "An error occurred while deleting the offices rating card."); + } + } + + /// + /// Get autofill data for offices rating card + /// + [HttpGet("autofill/{assetId}")] + public async Task> GetAutofillData(int assetId) + { + try + { + var result = await _service.GetAutofillDataAsync(assetId); + return Ok(result); + } + catch (KeyNotFoundException) + { + return NotFound($"Asset with ID {assetId} not found."); + } + catch (Exception ex) + { + _logger.LogError(ex, "Error retrieving autofill data for Asset ID {AssetId}", assetId); + return StatusCode(500, "An error occurred while retrieving autofill data."); + } + } + } +} \ No newline at end of file diff --git a/Data/AppDbContext.cs b/Data/AppDbContext.cs index b330b8c..2449d1a 100644 --- a/Data/AppDbContext.cs +++ b/Data/AppDbContext.cs @@ -1,5 +1,6 @@ using Microsoft.EntityFrameworkCore; using ValuationBackend.Models; +using ValuationBackend.Models.iteration2.RatingCards; namespace ValuationBackend.Data { @@ -55,6 +56,8 @@ public AppDbContext(DbContextOptions options) public DbSet DomesticRatingCards { get; set; } + public DbSet OfficesRatingCards { get; set; } + public DbSet Assets { get; set; } public DbSet PropertyCategories { get; set; } diff --git a/Extensions/RepositoryExtensions.cs b/Extensions/RepositoryExtensions.cs index 35706d3..8792be5 100644 --- a/Extensions/RepositoryExtensions.cs +++ b/Extensions/RepositoryExtensions.cs @@ -1,4 +1,6 @@ using ValuationBackend.Repositories; +using ValuationBackend.repositories; +using ValuationBackend.repositories.iteration2; namespace ValuationBackend.Extensions { @@ -22,6 +24,7 @@ public static IServiceCollection AddRepositories(this IServiceCollection service services.AddScoped(); services.AddScoped(); services.AddScoped(); + services.AddScoped(); services.AddScoped(); services.AddScoped(); services.AddScoped(); diff --git a/Extensions/ServiceExtensions.cs b/Extensions/ServiceExtensions.cs index 4e4bf45..6acb236 100644 --- a/Extensions/ServiceExtensions.cs +++ b/Extensions/ServiceExtensions.cs @@ -1,4 +1,5 @@ using ValuationBackend.Services; +using ValuationBackend.services.iteration2; namespace ValuationBackend.Extensions { @@ -15,6 +16,7 @@ public static IServiceCollection AddServices(this IServiceCollection services) services.AddScoped(); services.AddScoped(); services.AddScoped(); + services.AddScoped(); services.AddScoped(); services.AddScoped(); services.AddScoped(); diff --git a/Migrations/AddOfficesRatingCardTable.cs b/Migrations/AddOfficesRatingCardTable.cs new file mode 100644 index 0000000..db1a9de --- /dev/null +++ b/Migrations/AddOfficesRatingCardTable.cs @@ -0,0 +1,77 @@ +using System; +using Microsoft.EntityFrameworkCore.Migrations; + +namespace ValuationBackend.Migrations +{ + public partial class AddOfficesRatingCardTable : Migration + { + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.CreateTable( + name: "OfficesRatingCards", + columns: table => new + { + Id = table.Column(nullable: false) + .Annotation("SqlServer:Identity", "1, 1"), + AssetId = table.Column(nullable: false), + BuildingSelection = table.Column(maxLength: 100, nullable: true), + LocalAuthority = table.Column(maxLength: 200, nullable: true), + LocalAuthorityCode = table.Column(maxLength: 50, nullable: true), + AssessmentNumber = table.Column(maxLength: 50, nullable: true), + NewNumber = table.Column(maxLength: 50, nullable: true), + ObsoleteNumber = table.Column(maxLength: 50, nullable: true), + Owner = table.Column(maxLength: 200, nullable: true), + Description = table.Column(maxLength: 500, nullable: true), + WallType = table.Column(maxLength: 100, nullable: true), + FloorType = table.Column(maxLength: 100, nullable: true), + Conveniences = table.Column(maxLength: 100, nullable: true), + Condition = table.Column(maxLength: 50, nullable: true), + Age = table.Column(nullable: true), + AccessType = table.Column(maxLength: 100, nullable: true), + OfficeGrade = table.Column(maxLength: 50, nullable: true), + ParkingSpace = table.Column(maxLength: 200, nullable: true), + PropertySubCategory = table.Column(maxLength: 100, nullable: true), + PropertyType = table.Column(maxLength: 100, nullable: true), + WardNumber = table.Column(nullable: true), + RoadName = table.Column(maxLength: 200, nullable: true), + Date = table.Column(nullable: true), + Occupier = table.Column(maxLength: 200, nullable: true), + RentPM = table.Column(type: "decimal(18,2)", nullable: true), + Terms = table.Column(maxLength: 500, nullable: true), + FloorNumber = table.Column(nullable: true), + CeilingHeight = table.Column(type: "decimal(18,2)", nullable: true), + OfficeSuite = table.Column(maxLength: 1000, nullable: true), + TotalArea = table.Column(type: "decimal(18,2)", nullable: true), + UsableFloorArea = table.Column(type: "decimal(18,2)", nullable: true), + SuggestedRate = table.Column(type: "decimal(18,2)", nullable: true), + Notes = table.Column(maxLength: 2000, nullable: true), + CreatedAt = table.Column(nullable: false), + UpdatedAt = table.Column(nullable: true), + CreatedBy = table.Column(nullable: true), + UpdatedBy = table.Column(nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_OfficesRatingCards", x => x.Id); + table.ForeignKey( + name: "FK_OfficesRatingCards_Assets_AssetId", + column: x => x.AssetId, + principalTable: "Assets", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateIndex( + name: "IX_OfficesRatingCards_AssetId", + table: "OfficesRatingCards", + column: "AssetId", + unique: true); + } + + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropTable( + name: "OfficesRatingCards"); + } + } +} \ No newline at end of file diff --git a/Models/iteration2/DTOs/OfficesRatingCardDto.cs b/Models/iteration2/DTOs/OfficesRatingCardDto.cs new file mode 100644 index 0000000..ee79418 --- /dev/null +++ b/Models/iteration2/DTOs/OfficesRatingCardDto.cs @@ -0,0 +1,121 @@ +using System; +using System.ComponentModel.DataAnnotations; + +namespace ValuationBackend.Models.iteration2.DTOs +{ + public class OfficesRatingCardDto + { + public int Id { get; set; } + public int AssetId { get; set; } + public string BuildingSelection { get; set; } + public string LocalAuthority { get; set; } + public string LocalAuthorityCode { get; set; } + public string AssessmentNumber { get; set; } + public string NewNumber { get; set; } + public string ObsoleteNumber { get; set; } + public string Owner { get; set; } + public string Description { get; set; } + public string WallType { get; set; } + public string FloorType { get; set; } + public string Conveniences { get; set; } + public string Condition { get; set; } + public int? Age { get; set; } + public string AccessType { get; set; } + public string OfficeGrade { get; set; } + public string ParkingSpace { get; set; } + public string PropertySubCategory { get; set; } + public string PropertyType { get; set; } + public int? WardNumber { get; set; } + public string RoadName { get; set; } + public DateTime? Date { get; set; } + public string Occupier { get; set; } + public decimal? RentPM { get; set; } + public string Terms { get; set; } + public int? FloorNumber { get; set; } + public decimal? CeilingHeight { get; set; } + public string OfficeSuite { get; set; } + public decimal? TotalArea { get; set; } + public decimal? UsableFloorArea { get; set; } + public decimal? SuggestedRate { get; set; } + public string Notes { get; set; } + public DateTime CreatedAt { get; set; } + public DateTime? UpdatedAt { get; set; } + } + + public class CreateOfficesRatingCardDto + { + [Required] + public int AssetId { get; set; } + + public string BuildingSelection { get; set; } + public string LocalAuthority { get; set; } + public string LocalAuthorityCode { get; set; } + public string AssessmentNumber { get; set; } + public string NewNumber { get; set; } + public string ObsoleteNumber { get; set; } + public string Owner { get; set; } + public string Description { get; set; } + public string WallType { get; set; } + public string FloorType { get; set; } + public string Conveniences { get; set; } + public string Condition { get; set; } + public int? Age { get; set; } + public string AccessType { get; set; } + public string OfficeGrade { get; set; } + public string ParkingSpace { get; set; } + public string PropertySubCategory { get; set; } + public string PropertyType { get; set; } + public int? WardNumber { get; set; } + public string RoadName { get; set; } + public DateTime? Date { get; set; } + public string Occupier { get; set; } + public decimal? RentPM { get; set; } + public string Terms { get; set; } + public int? FloorNumber { get; set; } + public decimal? CeilingHeight { get; set; } + public string OfficeSuite { get; set; } + public decimal? TotalArea { get; set; } + public decimal? UsableFloorArea { get; set; } + public decimal? SuggestedRate { get; set; } + public string Notes { get; set; } + } + + public class UpdateOfficesRatingCardDto + { + public string BuildingSelection { get; set; } + public string LocalAuthority { get; set; } + public string LocalAuthorityCode { get; set; } + public string AssessmentNumber { get; set; } + public string ObsoleteNumber { get; set; } + public string WallType { get; set; } + public string FloorType { get; set; } + public string Conveniences { get; set; } + public string Condition { get; set; } + public int? Age { get; set; } + public string AccessType { get; set; } + public string OfficeGrade { get; set; } + public string ParkingSpace { get; set; } + public string PropertySubCategory { get; set; } + public string PropertyType { get; set; } + public int? WardNumber { get; set; } + public string RoadName { get; set; } + public DateTime? Date { get; set; } + public string Occupier { get; set; } + public decimal? RentPM { get; set; } + public string Terms { get; set; } + public int? FloorNumber { get; set; } + public decimal? CeilingHeight { get; set; } + public string OfficeSuite { get; set; } + public decimal? TotalArea { get; set; } + public decimal? UsableFloorArea { get; set; } + public decimal? SuggestedRate { get; set; } + public string Notes { get; set; } + } + + public class OfficesRatingCardAutofillDto + { + public string NewNumber { get; set; } + public string Owner { get; set; } + public string Description { get; set; } + } +} \ No newline at end of file diff --git a/Models/iteration2/RatingCards/OfficesRatingCard.cs b/Models/iteration2/RatingCards/OfficesRatingCard.cs new file mode 100644 index 0000000..eb61a35 --- /dev/null +++ b/Models/iteration2/RatingCards/OfficesRatingCard.cs @@ -0,0 +1,113 @@ +using System; +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; + +namespace ValuationBackend.Models.iteration2.RatingCards +{ + public class OfficesRatingCard + { + [Key] + public int Id { get; set; } + + [Required] + public int AssetId { get; set; } + + [ForeignKey("AssetId")] + public virtual Asset Asset { get; set; } + + [StringLength(100)] + public string BuildingSelection { get; set; } + + [StringLength(200)] + public string LocalAuthority { get; set; } + + [StringLength(50)] + public string LocalAuthorityCode { get; set; } + + [StringLength(50)] + public string AssessmentNumber { get; set; } + + [StringLength(50)] + public string NewNumber { get; set; } + + [StringLength(50)] + public string ObsoleteNumber { get; set; } + + [StringLength(200)] + public string Owner { get; set; } + + [StringLength(500)] + public string Description { get; set; } + + [StringLength(100)] + public string WallType { get; set; } + + [StringLength(100)] + public string FloorType { get; set; } + + [StringLength(100)] + public string Conveniences { get; set; } + + [StringLength(50)] + public string Condition { get; set; } + + public int? Age { get; set; } + + [StringLength(100)] + public string AccessType { get; set; } + + [StringLength(50)] + public string OfficeGrade { get; set; } + + [StringLength(200)] + public string ParkingSpace { get; set; } + + [StringLength(100)] + public string PropertySubCategory { get; set; } + + [StringLength(100)] + public string PropertyType { get; set; } + + public int? WardNumber { get; set; } + + [StringLength(200)] + public string RoadName { get; set; } + + public DateTime? Date { get; set; } + + [StringLength(200)] + public string Occupier { get; set; } + + [Column(TypeName = "decimal(18,2)")] + public decimal? RentPM { get; set; } + + [StringLength(500)] + public string Terms { get; set; } + + public int? FloorNumber { get; set; } + + [Column(TypeName = "decimal(18,2)")] + public decimal? CeilingHeight { get; set; } + + [StringLength(1000)] + public string OfficeSuite { get; set; } + + [Column(TypeName = "decimal(18,2)")] + public decimal? TotalArea { get; set; } + + [Column(TypeName = "decimal(18,2)")] + public decimal? UsableFloorArea { get; set; } + + [Column(TypeName = "decimal(18,2)")] + public decimal? SuggestedRate { get; set; } + + [StringLength(2000)] + public string Notes { get; set; } + + // Audit fields + public DateTime CreatedAt { get; set; } + public DateTime? UpdatedAt { get; set; } + public string CreatedBy { get; set; } + public string UpdatedBy { get; set; } + } +} \ No newline at end of file diff --git a/Profiles/OfficesRatingCardProfile.cs b/Profiles/OfficesRatingCardProfile.cs new file mode 100644 index 0000000..a9f84b1 --- /dev/null +++ b/Profiles/OfficesRatingCardProfile.cs @@ -0,0 +1,38 @@ +using AutoMapper; +using ValuationBackend.Models.iteration2.DTOs; +using ValuationBackend.Models.iteration2.RatingCards; + +namespace ValuationBackend.Profiles +{ + public class OfficesRatingCardProfile : Profile + { + public OfficesRatingCardProfile() + { + // Entity to DTO + CreateMap(); + + // Create DTO to Entity + CreateMap() + .ForMember(dest => dest.Id, opt => opt.Ignore()) + .ForMember(dest => dest.CreatedAt, opt => opt.Ignore()) + .ForMember(dest => dest.UpdatedAt, opt => opt.Ignore()) + .ForMember(dest => dest.CreatedBy, opt => opt.Ignore()) + .ForMember(dest => dest.UpdatedBy, opt => opt.Ignore()) + .ForMember(dest => dest.Asset, opt => opt.Ignore()); + + // Update DTO to Entity + CreateMap() + .ForMember(dest => dest.Id, opt => opt.Ignore()) + .ForMember(dest => dest.AssetId, opt => opt.Ignore()) + .ForMember(dest => dest.NewNumber, opt => opt.Ignore()) + .ForMember(dest => dest.Owner, opt => opt.Ignore()) + .ForMember(dest => dest.Description, opt => opt.Ignore()) + .ForMember(dest => dest.CreatedAt, opt => opt.Ignore()) + .ForMember(dest => dest.UpdatedAt, opt => opt.Ignore()) + .ForMember(dest => dest.CreatedBy, opt => opt.Ignore()) + .ForMember(dest => dest.UpdatedBy, opt => opt.Ignore()) + .ForMember(dest => dest.Asset, opt => opt.Ignore()) + .ForAllMembers(opts => opts.Condition((src, dest, srcMember) => srcMember != null)); + } + } +} \ No newline at end of file diff --git a/repositories/iteration2/IOfficesRatingCardRepository.cs b/repositories/iteration2/IOfficesRatingCardRepository.cs new file mode 100644 index 0000000..a2bb444 --- /dev/null +++ b/repositories/iteration2/IOfficesRatingCardRepository.cs @@ -0,0 +1,18 @@ +using System.Collections.Generic; +using System.Threading.Tasks; +using ValuationBackend.Models.iteration2.RatingCards; + +namespace ValuationBackend.repositories.iteration2 +{ + public interface IOfficesRatingCardRepository + { + Task> GetAllAsync(); + Task GetByIdAsync(int id); + Task GetByAssetIdAsync(int assetId); + Task CreateAsync(OfficesRatingCard officesRatingCard); + Task UpdateAsync(OfficesRatingCard officesRatingCard); + Task DeleteAsync(int id); + Task ExistsAsync(int id); + Task ExistsByAssetIdAsync(int assetId); + } +} \ No newline at end of file diff --git a/repositories/iteration2/OfficesRatingCardRepository.cs b/repositories/iteration2/OfficesRatingCardRepository.cs new file mode 100644 index 0000000..ec7fb17 --- /dev/null +++ b/repositories/iteration2/OfficesRatingCardRepository.cs @@ -0,0 +1,84 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Microsoft.EntityFrameworkCore; +using ValuationBackend.Data; +using ValuationBackend.Models.iteration2.RatingCards; + +namespace ValuationBackend.repositories.iteration2 +{ + public class OfficesRatingCardRepository : IOfficesRatingCardRepository + { + private readonly AppDbContext _context; + + public OfficesRatingCardRepository(AppDbContext context) + { + _context = context; + } + + public async Task> GetAllAsync() + { + return await _context.OfficesRatingCards + .Include(o => o.Asset) + .ToListAsync(); + } + + public async Task GetByIdAsync(int id) + { + return await _context.OfficesRatingCards + .Include(o => o.Asset) + .FirstOrDefaultAsync(o => o.Id == id); + } + + public async Task GetByAssetIdAsync(int assetId) + { + return await _context.OfficesRatingCards + .Include(o => o.Asset) + .FirstOrDefaultAsync(o => o.AssetId == assetId); + } + + public async Task CreateAsync(OfficesRatingCard officesRatingCard) + { + officesRatingCard.CreatedAt = DateTime.UtcNow; + _context.OfficesRatingCards.Add(officesRatingCard); + await _context.SaveChangesAsync(); + return officesRatingCard; + } + + public async Task UpdateAsync(OfficesRatingCard officesRatingCard) + { + officesRatingCard.UpdatedAt = DateTime.UtcNow; + _context.Entry(officesRatingCard).State = EntityState.Modified; + + // Don't update these fields + _context.Entry(officesRatingCard).Property(x => x.CreatedAt).IsModified = false; + _context.Entry(officesRatingCard).Property(x => x.CreatedBy).IsModified = false; + _context.Entry(officesRatingCard).Property(x => x.AssetId).IsModified = false; + + await _context.SaveChangesAsync(); + return officesRatingCard; + } + + public async Task DeleteAsync(int id) + { + var officesRatingCard = await _context.OfficesRatingCards.FindAsync(id); + if (officesRatingCard == null) + return false; + + _context.OfficesRatingCards.Remove(officesRatingCard); + await _context.SaveChangesAsync(); + return true; + } + + public async Task ExistsAsync(int id) + { + return await _context.OfficesRatingCards.AnyAsync(o => o.Id == id); + } + + public async Task ExistsByAssetIdAsync(int assetId) + { + return await _context.OfficesRatingCards.AnyAsync(o => o.AssetId == assetId); + } + } +} \ No newline at end of file diff --git a/services/iteration2/IOfficesRatingCardService.cs b/services/iteration2/IOfficesRatingCardService.cs new file mode 100644 index 0000000..bdc21d3 --- /dev/null +++ b/services/iteration2/IOfficesRatingCardService.cs @@ -0,0 +1,17 @@ +using System.Collections.Generic; +using System.Threading.Tasks; +using ValuationBackend.Models.iteration2.DTOs; + +namespace ValuationBackend.services.iteration2 +{ + public interface IOfficesRatingCardService + { + Task> GetAllAsync(); + Task GetByIdAsync(int id); + Task GetByAssetIdAsync(int assetId); + Task CreateAsync(CreateOfficesRatingCardDto dto); + Task UpdateAsync(int id, UpdateOfficesRatingCardDto dto); + Task DeleteAsync(int id); + Task GetAutofillDataAsync(int assetId); + } +} \ No newline at end of file diff --git a/services/iteration2/OfficesRatingCardService.cs b/services/iteration2/OfficesRatingCardService.cs new file mode 100644 index 0000000..6b85009 --- /dev/null +++ b/services/iteration2/OfficesRatingCardService.cs @@ -0,0 +1,134 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using AutoMapper; +using Microsoft.AspNetCore.Http; +using ValuationBackend.Models.iteration2.DTOs; +using ValuationBackend.Models.iteration2.RatingCards; +using ValuationBackend.repositories; +using ValuationBackend.repositories.iteration2; + +namespace ValuationBackend.services.iteration2 +{ + public class OfficesRatingCardService : IOfficesRatingCardService + { + private readonly IOfficesRatingCardRepository _repository; + private readonly IAssetRepository _assetRepository; + private readonly IMapper _mapper; + private readonly IHttpContextAccessor _httpContextAccessor; + + public OfficesRatingCardService( + IOfficesRatingCardRepository repository, + IAssetRepository assetRepository, + IMapper mapper, + IHttpContextAccessor httpContextAccessor) + { + _repository = repository; + _assetRepository = assetRepository; + _mapper = mapper; + _httpContextAccessor = httpContextAccessor; + } + + public async Task> GetAllAsync() + { + var entities = await _repository.GetAllAsync(); + return _mapper.Map>(entities); + } + + public async Task GetByIdAsync(int id) + { + var entity = await _repository.GetByIdAsync(id); + if (entity == null) + throw new KeyNotFoundException($"OfficesRatingCard with ID {id} not found."); + + return _mapper.Map(entity); + } + + public async Task GetByAssetIdAsync(int assetId) + { + var entity = await _repository.GetByAssetIdAsync(assetId); + if (entity == null) + throw new KeyNotFoundException($"OfficesRatingCard for Asset ID {assetId} not found."); + + return _mapper.Map(entity); + } + + public async Task CreateAsync(CreateOfficesRatingCardDto dto) + { + // Check if rating card already exists for this asset + if (await _repository.ExistsByAssetIdAsync(dto.AssetId)) + { + throw new InvalidOperationException($"OfficesRatingCard already exists for Asset ID {dto.AssetId}."); + } + + // Get asset details for auto-generation + var asset = await _assetRepository.GetByIdAsync(dto.AssetId); + if (asset == null) + { + throw new KeyNotFoundException($"Asset with ID {dto.AssetId} not found."); + } + + var entity = _mapper.Map(dto); + + // Auto-generate fields from asset if not provided + if (string.IsNullOrWhiteSpace(entity.NewNumber)) + entity.NewNumber = asset.NewNumber; + + if (string.IsNullOrWhiteSpace(entity.Owner)) + entity.Owner = asset.Owner; + + if (string.IsNullOrWhiteSpace(entity.Description)) + entity.Description = asset.Description; + + // Set audit fields + entity.CreatedBy = GetCurrentUser(); + entity.CreatedAt = DateTime.UtcNow; + + var created = await _repository.CreateAsync(entity); + return _mapper.Map(created); + } + + public async Task UpdateAsync(int id, UpdateOfficesRatingCardDto dto) + { + var entity = await _repository.GetByIdAsync(id); + if (entity == null) + throw new KeyNotFoundException($"OfficesRatingCard with ID {id} not found."); + + _mapper.Map(dto, entity); + + // Set audit fields + entity.UpdatedBy = GetCurrentUser(); + entity.UpdatedAt = DateTime.UtcNow; + + var updated = await _repository.UpdateAsync(entity); + return _mapper.Map(updated); + } + + public async Task DeleteAsync(int id) + { + return await _repository.DeleteAsync(id); + } + + public async Task GetAutofillDataAsync(int assetId) + { + var asset = await _assetRepository.GetByIdAsync(assetId); + if (asset == null) + { + throw new KeyNotFoundException($"Asset with ID {assetId} not found."); + } + + return new OfficesRatingCardAutofillDto + { + NewNumber = asset.NewNumber, + Owner = asset.Owner, + Description = asset.Description + }; + } + + private string GetCurrentUser() + { + return _httpContextAccessor.HttpContext?.User?.Identity?.Name ?? "System"; + } + } +} \ No newline at end of file