mirror of
https://github.com/Sea-Haven-Industries/shoc-backend.git
synced 2026-10-02 08:33:22 +00:00
commit
50a4bf57f8
53 changed files with 3038 additions and 1393 deletions
|
|
@ -1,9 +1,11 @@
|
|||
using Api.SeaHavenIndustries.DTOs;
|
||||
using Data.SeaHavenIndustries;
|
||||
using FluentValidation;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using SeaHaven.Services.Interfaces;
|
||||
using SeaHaven.Services.DTOs;
|
||||
using System.Security.Claims;
|
||||
|
||||
[ApiController]
|
||||
[Route("api/Account")]
|
||||
|
|
@ -33,7 +35,10 @@ public class AccountController : ControllerBase
|
|||
Website = dto.Website
|
||||
};
|
||||
|
||||
var userId = User.Identity?.Name ?? "system";
|
||||
var userId = User.FindFirstValue(ClaimTypes.NameIdentifier);
|
||||
if (userId == null)
|
||||
return Unauthorized(new Response { Status = "Error", Message = "User not authenticated" });
|
||||
|
||||
await _accountService.CreateAccountAsync(createDto, userId);
|
||||
|
||||
return Ok(new DataResponse
|
||||
|
|
@ -42,13 +47,14 @@ public class AccountController : ControllerBase
|
|||
Status = "200"
|
||||
});
|
||||
}
|
||||
catch (ValidationException vex)
|
||||
{
|
||||
var errors = string.Join(", ", vex.Errors.Select(e => e.ErrorMessage));
|
||||
return BadRequest(new Response { Status = "Validation Error", Message = errors });
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return BadRequest(new Response
|
||||
{
|
||||
Status = "Error",
|
||||
Message = ex.Message
|
||||
});
|
||||
return StatusCode(500, new Response { Status = "Error", Message = ex.Message });
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -68,7 +74,10 @@ public class AccountController : ControllerBase
|
|||
Website = dto.Website
|
||||
};
|
||||
|
||||
var userId = User.Identity?.Name ?? "system";
|
||||
var userId = User.FindFirstValue(ClaimTypes.NameIdentifier);
|
||||
if (userId == null)
|
||||
return Unauthorized(new Response { Status = "Error", Message = "User not authenticated" });
|
||||
|
||||
await _accountService.UpdateAccountAsync(dto.Id, updateDto, userId);
|
||||
|
||||
return Ok(new DataResponse
|
||||
|
|
@ -77,13 +86,18 @@ public class AccountController : ControllerBase
|
|||
Status = "200"
|
||||
});
|
||||
}
|
||||
catch (ValidationException vex)
|
||||
{
|
||||
var errors = string.Join(", ", vex.Errors.Select(e => e.ErrorMessage));
|
||||
return BadRequest(new Response { Status = "Validation Error", Message = errors });
|
||||
}
|
||||
catch (KeyNotFoundException)
|
||||
{
|
||||
return NotFound(new Response { Status = "Error", Message = "Account not found" });
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return BadRequest(new Response
|
||||
{
|
||||
Status = "Error",
|
||||
Message = ex.Message
|
||||
});
|
||||
return StatusCode(500, new Response { Status = "Error", Message = ex.Message });
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -95,7 +109,10 @@ public class AccountController : ControllerBase
|
|||
{
|
||||
try
|
||||
{
|
||||
var userId = User.Identity?.Name ?? "system";
|
||||
var userId = User.FindFirstValue(ClaimTypes.NameIdentifier);
|
||||
if (userId == null)
|
||||
return Unauthorized(new Response { Status = "Error", Message = "User not authenticated" });
|
||||
|
||||
await _accountService.DeleteAccountAsync(id, userId);
|
||||
|
||||
return Ok(new DataResponse
|
||||
|
|
@ -104,13 +121,13 @@ public class AccountController : ControllerBase
|
|||
Status = "200"
|
||||
});
|
||||
}
|
||||
catch (KeyNotFoundException)
|
||||
{
|
||||
return NotFound(new Response { Status = "Error", Message = "Account not found" });
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return BadRequest(new Response
|
||||
{
|
||||
Status = "Error",
|
||||
Message = ex.Message
|
||||
});
|
||||
return StatusCode(500, new Response { Status = "Error", Message = ex.Message });
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,11 +1,13 @@
|
|||
using Api.SeaHavenIndustries.DTOs;
|
||||
using Data.SeaHavenIndustries;
|
||||
using FluentValidation;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Identity;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using SeaHaven.Services.Interfaces;
|
||||
using SeaHaven.Services.DTOs;
|
||||
using System.Security.Claims;
|
||||
|
||||
namespace Api.SeaHavenIndustries.Controllers
|
||||
{
|
||||
|
|
@ -35,14 +37,22 @@ namespace Api.SeaHavenIndustries.Controllers
|
|||
LocationId = asset.LocationId
|
||||
};
|
||||
|
||||
var userId = User.Identity?.Name ?? "system";
|
||||
var userId = User.FindFirstValue(ClaimTypes.NameIdentifier);
|
||||
if (userId == null)
|
||||
return Unauthorized(new Response { Status = "Error", Message = "User not authenticated" });
|
||||
|
||||
await _assetService.CreateAssetAsync(createDto, userId);
|
||||
|
||||
return Ok(new DataResponse { Message = "Asset Created Successfully", Status = "200" });
|
||||
}
|
||||
catch (ValidationException vex)
|
||||
{
|
||||
var errors = string.Join(", ", vex.Errors.Select(e => e.ErrorMessage));
|
||||
return BadRequest(new Response { Status = "Validation Error", Message = errors });
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return BadRequest(new Response { Status = "Error", Message = ex.Message });
|
||||
return StatusCode(500, new Response { Status = "Error", Message = ex.Message });
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -58,14 +68,26 @@ namespace Api.SeaHavenIndustries.Controllers
|
|||
LocationId = asset.LocationId
|
||||
};
|
||||
|
||||
var userId = User.Identity?.Name ?? "system";
|
||||
var userId = User.FindFirstValue(ClaimTypes.NameIdentifier);
|
||||
if (userId == null)
|
||||
return Unauthorized(new Response { Status = "Error", Message = "User not authenticated" });
|
||||
|
||||
await _assetService.UpdateAssetAsync(asset.Id, updateDto, userId);
|
||||
|
||||
return Ok(new DataResponse { Message = "Asset Updated Successfully", Status = "200" });
|
||||
}
|
||||
catch (ValidationException vex)
|
||||
{
|
||||
var errors = string.Join(", ", vex.Errors.Select(e => e.ErrorMessage));
|
||||
return BadRequest(new Response { Status = "Validation Error", Message = errors });
|
||||
}
|
||||
catch (KeyNotFoundException)
|
||||
{
|
||||
return NotFound(new Response { Status = "Error", Message = "Asset not found" });
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return BadRequest(new Response { Status = "Error", Message = ex.Message });
|
||||
return StatusCode(500, new Response { Status = "Error", Message = ex.Message });
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -75,13 +97,20 @@ namespace Api.SeaHavenIndustries.Controllers
|
|||
{
|
||||
try
|
||||
{
|
||||
var userId = User.Identity?.Name ?? "system";
|
||||
await _assetService.DeleteAssetAsync(id, userId);
|
||||
var userId = User.FindFirstValue(ClaimTypes.NameIdentifier);
|
||||
if (userId == null)
|
||||
return Unauthorized(new Response { Status = "Error", Message = "User not authenticated" });
|
||||
|
||||
await _assetService.DeleteAssetAsync(id, userId!);
|
||||
return Ok(new DataResponse { Message = "Asset Deleted Successfully", Status = "200" });
|
||||
}
|
||||
catch (KeyNotFoundException)
|
||||
{
|
||||
return NotFound(new Response { Status = "Error", Message = "Asset not found" });
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return BadRequest(new Response { Status = "Error", Message = ex.Message });
|
||||
return StatusCode(500, new Response { Status = "Error", Message = ex.Message });
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
using Api.SeaHavenIndustries.DTOs;
|
||||
using Data.SeaHavenIndustries;
|
||||
using FluentValidation;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Identity;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
|
|
@ -34,15 +35,22 @@ namespace Api.SeaHavenIndustries.Controllers
|
|||
{
|
||||
try
|
||||
{
|
||||
// ✅ Use DTO mapping method instead of manual property assignment
|
||||
var userId = _userManager.GetUserId(User) ?? "system";
|
||||
var userId = _userManager.GetUserId(User);
|
||||
if (userId == null)
|
||||
return Unauthorized(new Response { Status = "Error", Message = "User not authenticated" });
|
||||
|
||||
await _contactService.CreateContactAsync(contacts.ToServiceCreateDTO(), userId);
|
||||
|
||||
return Ok(new DataResponse { Message = "Updated Successfully", Status = "200" });
|
||||
return Ok(new DataResponse { Message = "Contact created successfully", Status = "200" });
|
||||
}
|
||||
catch (ValidationException vex)
|
||||
{
|
||||
var errors = string.Join(", ", vex.Errors.Select(e => e.ErrorMessage));
|
||||
return BadRequest(new Response { Status = "Validation Error", Message = errors });
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return BadRequest(new Response { Status = "Error", Message = ex.Message });
|
||||
return StatusCode(500, new Response { Status = "Error", Message = ex.Message });
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -52,19 +60,26 @@ namespace Api.SeaHavenIndustries.Controllers
|
|||
{
|
||||
try
|
||||
{
|
||||
// ✅ Use DTO mapping method instead of manual property assignment
|
||||
var userId = _userManager.GetUserId(User) ?? "system";
|
||||
var userId = _userManager.GetUserId(User);
|
||||
if (userId == null)
|
||||
return Unauthorized(new Response { Status = "Error", Message = "User not authenticated" });
|
||||
|
||||
await _contactService.UpdateContactAsync(contacts.Id, contacts.ToServiceUpdateDTO(), userId);
|
||||
|
||||
return Ok(new DataResponse { Message = "Updated Successfully", Status = "200" });
|
||||
return Ok(new DataResponse { Message = "Contact updated successfully", Status = "200" });
|
||||
}
|
||||
catch (ValidationException vex)
|
||||
{
|
||||
var errors = string.Join(", ", vex.Errors.Select(e => e.ErrorMessage));
|
||||
return BadRequest(new Response { Status = "Validation Error", Message = errors });
|
||||
}
|
||||
catch (KeyNotFoundException)
|
||||
{
|
||||
return BadRequest(new Response { Status = "Error", Message = "Contact not found" });
|
||||
return NotFound(new Response { Status = "Error", Message = "Contact not found" });
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return BadRequest(new Response { Status = "Error", Message = ex.Message });
|
||||
return StatusCode(500, new Response { Status = "Error", Message = ex.Message });
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -74,30 +89,28 @@ namespace Api.SeaHavenIndustries.Controllers
|
|||
{
|
||||
try
|
||||
{
|
||||
var userId = _userManager.GetUserId(User) ?? "system";
|
||||
var userId = _userManager.GetUserId(User);
|
||||
if (userId == null)
|
||||
return Unauthorized(new Response { Status = "Error", Message = "User not authenticated" });
|
||||
|
||||
// ✅ Call Service layer (not DataService directly)
|
||||
await _contactService.DeleteContactAsync(id, userId);
|
||||
|
||||
return Ok(new DataResponse { Message = "Removed Successfully", Status = "200" });
|
||||
return Ok(new DataResponse { Message = "Contact deleted successfully", Status = "200" });
|
||||
}
|
||||
catch (KeyNotFoundException)
|
||||
{
|
||||
return BadRequest(new Response { Status = "Error", Message = "ID not matched!" });
|
||||
return NotFound(new Response { Status = "Error", Message = "Contact not found" });
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return BadRequest(new Response { Status = "Error", Message = ex.Message });
|
||||
return StatusCode(500, new Response { Status = "Error", Message = ex.Message });
|
||||
}
|
||||
}
|
||||
[HttpGet]
|
||||
[Route("GetContactList")]
|
||||
public async Task<IActionResult> GetContactList(string? search ="", int page = 1, int pageSize = 10)
|
||||
{
|
||||
// ✅ Call Service layer
|
||||
var pagedResult = await _contactService.GetContactsPagedAsync(page, pageSize, search);
|
||||
|
||||
// ✅ Use DTO mapping method (NO manual transformation in controller)
|
||||
var data = ContactListItem_DTO.FromServiceDTOs(pagedResult.Items);
|
||||
var totalPages = (int)Math.Ceiling(pagedResult.TotalCount / (double)pageSize);
|
||||
|
||||
|
|
@ -116,7 +129,6 @@ namespace Api.SeaHavenIndustries.Controllers
|
|||
[Route("GetContacts")]
|
||||
public async Task<IActionResult> GetContacts()
|
||||
{
|
||||
// ✅ Call Service layer
|
||||
var data = await _contactService.GetAllContactsAsync();
|
||||
return Ok(data);
|
||||
}
|
||||
|
|
@ -124,24 +136,15 @@ namespace Api.SeaHavenIndustries.Controllers
|
|||
[Route("GetLocations")]
|
||||
public async Task<IActionResult> GetLocations()
|
||||
{
|
||||
// ✅ Call Service layer
|
||||
var locations = await _locationService.GetAllLocationsAsync();
|
||||
|
||||
// ✅ Use DTO mapping method (NO manual transformation in controller)
|
||||
var data = LocationViewModel_DTO.FromServiceDTOs(locations);
|
||||
return Ok(data);
|
||||
}
|
||||
|
||||
// TODO: ARCHITECTURAL VIOLATION - These methods should NOT be in ContactController
|
||||
// GetCategories should be in a CategoryController with ICategoryService
|
||||
// GetUsers should be in a UserController with IUserService
|
||||
// For now, commenting out to prevent direct database access
|
||||
|
||||
[HttpGet]
|
||||
[Route("GetCategories")]
|
||||
public async Task<IActionResult> GetCategories()
|
||||
{
|
||||
// ❌ TEMPORARY: Needs ICategoryService
|
||||
return StatusCode(501, new Response { Status = "Error", Message = "Not Implemented - Needs CategoryService" });
|
||||
}
|
||||
|
||||
|
|
@ -149,11 +152,8 @@ namespace Api.SeaHavenIndustries.Controllers
|
|||
[Route("GetUsers")]
|
||||
public async Task<IActionResult> GetUsers()
|
||||
{
|
||||
// ❌ TEMPORARY: Needs IUserService
|
||||
return StatusCode(501, new Response { Status = "Error", Message = "Not Implemented - Needs UserService" });
|
||||
}
|
||||
// TODO: ARCHITECTURAL VIOLATION - Location CRUD methods should NOT be in ContactController
|
||||
// These should be in LocationController with ILocationService
|
||||
|
||||
[HttpPost]
|
||||
[Route("AddLocation")]
|
||||
|
|
@ -161,7 +161,6 @@ namespace Api.SeaHavenIndustries.Controllers
|
|||
{
|
||||
try
|
||||
{
|
||||
// ✅ Use Service layer with DTO mapping
|
||||
var userId = _userManager.GetUserId(User) ?? "system";
|
||||
await _locationService.CreateLocationAsync(model.ToServiceCreateDTO(), userId);
|
||||
return Ok(new DataResponse { Message = "Updated Successfully", Status = "200" });
|
||||
|
|
@ -178,7 +177,6 @@ namespace Api.SeaHavenIndustries.Controllers
|
|||
{
|
||||
try
|
||||
{
|
||||
// ✅ Use Service layer with DTO mapping
|
||||
var userId = _userManager.GetUserId(User) ?? "system";
|
||||
await _locationService.UpdateLocationAsync(model.Id, model.ToServiceUpdateDTO(), userId);
|
||||
return Ok(new DataResponse { Message = "Updated Successfully", Status = "200" });
|
||||
|
|
@ -199,7 +197,6 @@ namespace Api.SeaHavenIndustries.Controllers
|
|||
{
|
||||
try
|
||||
{
|
||||
// ✅ Use Service layer
|
||||
var userId = _userManager.GetUserId(User) ?? "system";
|
||||
await _locationService.DeleteLocationAsync(Id, userId);
|
||||
return Ok(new DataResponse { Message = "Updated Successfully", Status = "200" });
|
||||
|
|
@ -218,10 +215,7 @@ namespace Api.SeaHavenIndustries.Controllers
|
|||
[Route("GetAddressbook")]
|
||||
public async Task<IActionResult> GetAddressbook(string? search = "", int page = 1, int pageSize = 10)
|
||||
{
|
||||
// ✅ Call Service layer
|
||||
var pagedResult = await _locationService.GetLocationsPagedAsync(page, pageSize, search);
|
||||
|
||||
// ✅ Use DTO mapping method (NO manual transformation in controller)
|
||||
var data = LocationListItem_DTO.FromServiceDTOs(pagedResult.Items);
|
||||
var totalPages = (int)Math.Ceiling(pagedResult.TotalCount / (double)pageSize);
|
||||
|
||||
|
|
|
|||
|
|
@ -1,9 +1,11 @@
|
|||
using Api.SeaHavenIndustries.DTOs;
|
||||
using Data.SeaHavenIndustries;
|
||||
using FluentValidation;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using SeaHaven.Services.Interfaces;
|
||||
using SeaHaven.Services.DTOs;
|
||||
using System.Security.Claims;
|
||||
|
||||
[ApiController]
|
||||
[Route("api/Employee")]
|
||||
|
|
@ -18,9 +20,10 @@ public class EmployeeController : Controller
|
|||
}
|
||||
|
||||
// -----------------------------------
|
||||
// POST: api/Employee/AddEmployees
|
||||
// POST: api/Employee/AddEmployees or /Create
|
||||
// -----------------------------------
|
||||
[HttpPost("AddEmployees")]
|
||||
[HttpPost("Create")]
|
||||
public async Task<IActionResult> AddEmployees(AddEmployee_DTO dto)
|
||||
{
|
||||
try
|
||||
|
|
@ -35,21 +38,30 @@ public class EmployeeController : Controller
|
|||
Phone = dto.Phone
|
||||
};
|
||||
|
||||
var userId = User.Identity?.Name ?? "system";
|
||||
var userId = User.FindFirstValue(ClaimTypes.NameIdentifier);
|
||||
if (userId == null)
|
||||
return Unauthorized(new Response { Status = "Error", Message = "User not authenticated" });
|
||||
|
||||
await _employeeService.CreateEmployeeAsync(createDto, userId);
|
||||
|
||||
return Ok(new { Message = "Employee added successfully" });
|
||||
}
|
||||
catch (ValidationException vex)
|
||||
{
|
||||
var errors = string.Join(", ", vex.Errors.Select(e => e.ErrorMessage));
|
||||
return BadRequest(new Response { Status = "Validation Error", Message = errors });
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return BadRequest(new { Message = ex.Message });
|
||||
return StatusCode(500, new Response { Status = "Error", Message = ex.Message });
|
||||
}
|
||||
}
|
||||
|
||||
// -----------------------------------
|
||||
// POST: api/Employee/EditEmployees
|
||||
// POST: api/Employee/EditEmployees or /Update
|
||||
// -----------------------------------
|
||||
[HttpPost("EditEmployees")]
|
||||
[HttpPost("Update")]
|
||||
public async Task<IActionResult> EditEmployees(EditEmployee_DTO dto)
|
||||
{
|
||||
try
|
||||
|
|
@ -61,14 +73,26 @@ public class EmployeeController : Controller
|
|||
JobTitleId = dto.JobTitleId
|
||||
};
|
||||
|
||||
var userId = User.Identity?.Name ?? "system";
|
||||
var userId = User.FindFirstValue(ClaimTypes.NameIdentifier);
|
||||
if (userId == null)
|
||||
return Unauthorized(new Response { Status = "Error", Message = "User not authenticated" });
|
||||
|
||||
await _employeeService.UpdateEmployeeAsync(dto.Id, updateDto, userId);
|
||||
|
||||
return Ok(new { Message = "Employee updated successfully" });
|
||||
}
|
||||
catch (ValidationException vex)
|
||||
{
|
||||
var errors = string.Join(", ", vex.Errors.Select(e => e.ErrorMessage));
|
||||
return BadRequest(new Response { Status = "Validation Error", Message = errors });
|
||||
}
|
||||
catch (KeyNotFoundException)
|
||||
{
|
||||
return NotFound(new Response { Status = "Error", Message = "Employee not found" });
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return BadRequest(new { Message = ex.Message });
|
||||
return StatusCode(500, new Response { Status = "Error", Message = ex.Message });
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -106,14 +130,50 @@ public class EmployeeController : Controller
|
|||
return Ok(viewModel);
|
||||
}
|
||||
// -----------------------------------
|
||||
// GET: api/Employee/DeleteEmployees?id=5
|
||||
// GET: api/Employee/GetById?id=5
|
||||
// -----------------------------------
|
||||
[HttpGet("GetById")]
|
||||
public async Task<IActionResult> GetById(int id)
|
||||
{
|
||||
try
|
||||
{
|
||||
var employee = await _employeeService.GetEmployeeByIdAsync(id);
|
||||
|
||||
return Ok(new
|
||||
{
|
||||
id = employee.Id,
|
||||
firstName = employee.FirstName,
|
||||
lastName = employee.LastName,
|
||||
jobTitle = employee.JobTitleName ?? "",
|
||||
email = employee.Email ?? "",
|
||||
phone = employee.Phone ?? "",
|
||||
department = employee.DepartmentName ?? "",
|
||||
status = "Active"
|
||||
});
|
||||
}
|
||||
catch (KeyNotFoundException)
|
||||
{
|
||||
return NotFound(new Response { Status = "Error", Message = "Employee not found" });
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return StatusCode(500, new Response { Status = "Error", Message = ex.Message });
|
||||
}
|
||||
}
|
||||
|
||||
// -----------------------------------
|
||||
// GET: api/Employee/DeleteEmployees or /Delete?id=5
|
||||
// -----------------------------------
|
||||
[HttpGet("DeleteEmployees")]
|
||||
[HttpPost("Delete")]
|
||||
public async Task<IActionResult> DeleteEmployees(int id)
|
||||
{
|
||||
try
|
||||
{
|
||||
var userId = User.Identity?.Name ?? "system";
|
||||
var userId = User.FindFirstValue(ClaimTypes.NameIdentifier);
|
||||
if (userId == null)
|
||||
return Unauthorized(new Response { Status = "Error", Message = "User not authenticated" });
|
||||
|
||||
await _employeeService.DeleteEmployeeAsync(id, userId);
|
||||
|
||||
return Ok(new DataResponse
|
||||
|
|
@ -130,14 +190,31 @@ public class EmployeeController : Controller
|
|||
Message = ex.Message
|
||||
});
|
||||
}
|
||||
catch (KeyNotFoundException)
|
||||
{
|
||||
return NotFound(new Response { Status = "Error", Message = "Employee not found" });
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return BadRequest(new Response
|
||||
{
|
||||
Status = "Error",
|
||||
Message = ex.Message
|
||||
});
|
||||
return StatusCode(500, new Response { Status = "Error", Message = ex.Message });
|
||||
}
|
||||
}
|
||||
|
||||
// -----------------------------------
|
||||
// GET: api/Employee/Dropdown
|
||||
// -----------------------------------
|
||||
[HttpGet("Dropdown")]
|
||||
public async Task<IActionResult> GetDropdown()
|
||||
{
|
||||
var pagedResult = await _employeeService.GetEmployeesPagedAsync(1, 1000, "");
|
||||
|
||||
var data = pagedResult.Items.Select(e => new
|
||||
{
|
||||
id = e.Id,
|
||||
name = $"{e.FirstName} {e.LastName}".Trim()
|
||||
});
|
||||
|
||||
return Ok(data);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
using System.Security.Claims;
|
||||
using Api.SeaHavenIndustries.DTOs;
|
||||
using Data.SeaHavenIndustries;
|
||||
using FluentValidation;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Identity;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
|
|
@ -157,44 +158,75 @@ public class FollowUpController : ControllerBase
|
|||
[HttpPost("Create")]
|
||||
public async Task<IActionResult> Create(FollowUp_DTO dto)
|
||||
{
|
||||
var userId = User.FindFirstValue(ClaimTypes.NameIdentifier);
|
||||
|
||||
var model = new FollowUps
|
||||
try
|
||||
{
|
||||
Reason = dto.Reason,
|
||||
ScheduleDate = dto.ScheduleDate,
|
||||
ScheduleStartTime = dto.ScheduleStartTime,
|
||||
EmployeeId = dto.EmployeeId,
|
||||
WorkOrderId = dto.WorkOrderId,
|
||||
AccountId = dto.AccountId,
|
||||
LocationId = dto.LocationId,
|
||||
Status = "Open",
|
||||
CreatedDate = DateTime.UtcNow,
|
||||
createdby = userId
|
||||
};
|
||||
var userId = User.FindFirstValue(ClaimTypes.NameIdentifier);
|
||||
if (userId == null)
|
||||
return Unauthorized(new Response { Status = "Error", Message = "User not authenticated" });
|
||||
|
||||
await _followUpDataService.AddAsync(model);
|
||||
var model = new FollowUps
|
||||
{
|
||||
Reason = dto.Reason,
|
||||
ScheduleDate = dto.ScheduleDate,
|
||||
ScheduleStartTime = dto.ScheduleStartTime,
|
||||
EmployeeId = dto.EmployeeId,
|
||||
WorkOrderId = dto.WorkOrderId,
|
||||
AccountId = dto.AccountId,
|
||||
LocationId = dto.LocationId,
|
||||
Status = "Open",
|
||||
CreatedDate = DateTime.UtcNow,
|
||||
createdby = userId
|
||||
};
|
||||
|
||||
return Ok(new DataResponse { Status = "200", Message = "Follow Up created", Data = model.Id });
|
||||
await _followUpDataService.AddAsync(model);
|
||||
|
||||
return Ok(new DataResponse { Status = "200", Message = "Follow Up created", Data = model.Id });
|
||||
}
|
||||
catch (ValidationException vex)
|
||||
{
|
||||
var errors = string.Join(", ", vex.Errors.Select(e => e.ErrorMessage));
|
||||
return BadRequest(new Response { Status = "Validation Error", Message = errors });
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return StatusCode(500, new Response { Status = "Error", Message = ex.Message });
|
||||
}
|
||||
}
|
||||
|
||||
[HttpPost("Update")]
|
||||
public async Task<IActionResult> Update(FollowUp_DTO dto)
|
||||
{
|
||||
var model = await _followUpDataService.GetByIdAsync(dto.Id);
|
||||
if (model == null) return NotFound();
|
||||
try
|
||||
{
|
||||
var model = await _followUpDataService.GetByIdAsync(dto.Id);
|
||||
if (model == null)
|
||||
return NotFound(new Response { Status = "Error", Message = "Follow Up not found" });
|
||||
|
||||
model.Reason = dto.Reason;
|
||||
model.ScheduleDate = dto.ScheduleDate;
|
||||
model.ScheduleStartTime = dto.ScheduleStartTime;
|
||||
model.EmployeeId = dto.EmployeeId;
|
||||
model.WorkOrderId = dto.WorkOrderId;
|
||||
model.AccountId = dto.AccountId;
|
||||
model.LocationId = dto.LocationId;
|
||||
model.LastModificationTime = DateTime.UtcNow;
|
||||
model.Reason = dto.Reason;
|
||||
model.ScheduleDate = dto.ScheduleDate;
|
||||
model.ScheduleStartTime = dto.ScheduleStartTime;
|
||||
model.EmployeeId = dto.EmployeeId;
|
||||
model.WorkOrderId = dto.WorkOrderId;
|
||||
model.AccountId = dto.AccountId;
|
||||
model.LocationId = dto.LocationId;
|
||||
model.LastModificationTime = DateTime.UtcNow;
|
||||
|
||||
await _followUpDataService.UpdateAsync(model);
|
||||
return Ok(new DataResponse { Status = "200", Message = "Follow Up updated" });
|
||||
await _followUpDataService.UpdateAsync(model);
|
||||
return Ok(new DataResponse { Status = "200", Message = "Follow Up updated" });
|
||||
}
|
||||
catch (ValidationException vex)
|
||||
{
|
||||
var errors = string.Join(", ", vex.Errors.Select(e => e.ErrorMessage));
|
||||
return BadRequest(new Response { Status = "Validation Error", Message = errors });
|
||||
}
|
||||
catch (KeyNotFoundException)
|
||||
{
|
||||
return NotFound(new Response { Status = "Error", Message = "Follow Up not found" });
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return StatusCode(500, new Response { Status = "Error", Message = ex.Message });
|
||||
}
|
||||
}
|
||||
|
||||
[HttpPost("Complete")]
|
||||
|
|
@ -226,11 +258,23 @@ public class FollowUpController : ControllerBase
|
|||
[HttpPost("Delete")]
|
||||
public async Task<IActionResult> Delete(int id)
|
||||
{
|
||||
var model = await _followUpDataService.GetByIdAsync(id);
|
||||
if (model == null) return NotFound();
|
||||
try
|
||||
{
|
||||
var model = await _followUpDataService.GetByIdAsync(id);
|
||||
if (model == null)
|
||||
return NotFound(new Response { Status = "Error", Message = "Follow Up not found" });
|
||||
|
||||
await _followUpDataService.DeleteAsync(id);
|
||||
await _followUpDataService.DeleteAsync(id);
|
||||
|
||||
return Ok(new DataResponse { Status = "200", Message = "Follow Up deleted" });
|
||||
return Ok(new DataResponse { Status = "200", Message = "Follow Up deleted" });
|
||||
}
|
||||
catch (KeyNotFoundException)
|
||||
{
|
||||
return NotFound(new Response { Status = "Error", Message = "Follow Up not found" });
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return StatusCode(500, new Response { Status = "Error", Message = ex.Message });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,11 +1,13 @@
|
|||
using Api.SeaHavenIndustries.DTOs;
|
||||
using Data.SeaHavenIndustries;
|
||||
using FluentValidation;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using SeaHaven.Services.Interfaces;
|
||||
using SeaHaven.Services.DTOs;
|
||||
using SeaHaven.DataServices.Interfaces;
|
||||
using System.Security.Claims;
|
||||
|
||||
namespace Api.SeaHavenIndustries.Controllers
|
||||
{
|
||||
|
|
@ -108,9 +110,14 @@ namespace Api.SeaHavenIndustries.Controllers
|
|||
await _locationDataService.AddAsync(location);
|
||||
return Ok(new DataResponse { Message = "Location Created Successfully", Status = "200" });
|
||||
}
|
||||
catch (ValidationException vex)
|
||||
{
|
||||
var errors = string.Join(", ", vex.Errors.Select(e => e.ErrorMessage));
|
||||
return BadRequest(new Response { Status = "Validation Error", Message = errors });
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return BadRequest(new Response { Status = "Error", Message = ex.Message });
|
||||
return StatusCode(500, new Response { Status = "Error", Message = ex.Message });
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -121,14 +128,14 @@ namespace Api.SeaHavenIndustries.Controllers
|
|||
{
|
||||
var exist = await _locationDataService.GetByIdAsync(id);
|
||||
if (exist == null)
|
||||
return BadRequest(new Response { Status = "Error", Message = "Location not found" });
|
||||
return NotFound(new Response { Status = "Error", Message = "Location not found" });
|
||||
|
||||
exist.Name = model.Name;
|
||||
exist.Title = model.Title;
|
||||
exist.Address1 = model.Address;
|
||||
exist.City = model.City;
|
||||
exist.State = model.State;
|
||||
exist.Zip = model.ZipCode; // This is a property, not a method
|
||||
exist.Zip = model.ZipCode;
|
||||
exist.PhoneNumber = model.Phone;
|
||||
exist.Email = model.ContactEmail;
|
||||
exist.Status = model.Status;
|
||||
|
|
@ -137,9 +144,18 @@ namespace Api.SeaHavenIndustries.Controllers
|
|||
await _locationDataService.UpdateAsync(exist);
|
||||
return Ok(new DataResponse { Message = "Location Updated Successfully", Status = "200" });
|
||||
}
|
||||
catch (ValidationException vex)
|
||||
{
|
||||
var errors = string.Join(", ", vex.Errors.Select(e => e.ErrorMessage));
|
||||
return BadRequest(new Response { Status = "Validation Error", Message = errors });
|
||||
}
|
||||
catch (KeyNotFoundException)
|
||||
{
|
||||
return NotFound(new Response { Status = "Error", Message = "Location not found" });
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return BadRequest(new Response { Status = "Error", Message = ex.Message });
|
||||
return StatusCode(500, new Response { Status = "Error", Message = ex.Message });
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -150,14 +166,18 @@ namespace Api.SeaHavenIndustries.Controllers
|
|||
{
|
||||
var exist = await _locationDataService.GetByIdAsync(id);
|
||||
if (exist == null)
|
||||
return BadRequest(new Response { Status = "Error", Message = "Location not found" });
|
||||
return NotFound(new Response { Status = "Error", Message = "Location not found" });
|
||||
|
||||
await _locationDataService.DeleteAsync(id);
|
||||
return Ok(new DataResponse { Message = "Location Deleted Successfully", Status = "200" });
|
||||
}
|
||||
catch (KeyNotFoundException)
|
||||
{
|
||||
return NotFound(new Response { Status = "Error", Message = "Location not found" });
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return BadRequest(new Response { Status = "Error", Message = ex.Message });
|
||||
return StatusCode(500, new Response { Status = "Error", Message = ex.Message });
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,9 +1,11 @@
|
|||
using Api.SeaHavenIndustries.DTOs;
|
||||
using Data.SeaHavenIndustries;
|
||||
using FluentValidation;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using SeaHaven.Services.Interfaces;
|
||||
using SeaHaven.Services.DTOs;
|
||||
using System.Security.Claims;
|
||||
|
||||
[ApiController]
|
||||
[Route("api/PMSchedule")]
|
||||
|
|
@ -36,12 +38,46 @@ public class PMScheduleController : ControllerBase
|
|||
return Ok(data);
|
||||
}
|
||||
|
||||
[HttpGet("GetById")]
|
||||
public async Task<IActionResult> GetById([FromQuery] int id)
|
||||
{
|
||||
try
|
||||
{
|
||||
var pmSchedule = await _pmScheduleService.GetPMScheduleByIdAsync(id);
|
||||
if (pmSchedule == null)
|
||||
return NotFound(new Response { Status = "Error", Message = "PM Schedule not found" });
|
||||
|
||||
return Ok(new
|
||||
{
|
||||
Id = pmSchedule.Id,
|
||||
Name = pmSchedule.Name,
|
||||
Frequency = pmSchedule.Frequency,
|
||||
StartDate = pmSchedule.StartDate,
|
||||
IsActive = pmSchedule.IsActive,
|
||||
AssetId = pmSchedule.AssetId,
|
||||
LocationId = pmSchedule.LocationId,
|
||||
Asset = pmSchedule.AssetName,
|
||||
Location = pmSchedule.LocationName
|
||||
});
|
||||
}
|
||||
catch (KeyNotFoundException)
|
||||
{
|
||||
return NotFound(new Response { Status = "Error", Message = "PM Schedule not found" });
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return StatusCode(500, new Response { Status = "Error", Message = ex.Message });
|
||||
}
|
||||
}
|
||||
|
||||
[HttpPost("Save")]
|
||||
public async Task<IActionResult> Save(PMSchedule_DTO dto)
|
||||
{
|
||||
try
|
||||
{
|
||||
var userId = User.Identity?.Name ?? "system";
|
||||
var userId = User.FindFirstValue(ClaimTypes.NameIdentifier);
|
||||
if (userId == null)
|
||||
return Unauthorized(new Response { Status = "Error", Message = "User not authenticated" });
|
||||
|
||||
if (dto.Id == 0)
|
||||
{
|
||||
|
|
@ -72,9 +108,40 @@ public class PMScheduleController : ControllerBase
|
|||
|
||||
return Ok();
|
||||
}
|
||||
catch (ValidationException vex)
|
||||
{
|
||||
var errors = string.Join(", ", vex.Errors.Select(e => e.ErrorMessage));
|
||||
return BadRequest(new Response { Status = "Validation Error", Message = errors });
|
||||
}
|
||||
catch (KeyNotFoundException)
|
||||
{
|
||||
return NotFound(new Response { Status = "Error", Message = "PM Schedule not found" });
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return BadRequest(new { Message = ex.Message });
|
||||
return StatusCode(500, new Response { Status = "Error", Message = ex.Message });
|
||||
}
|
||||
}
|
||||
|
||||
[HttpPost("Delete")]
|
||||
public async Task<IActionResult> Delete([FromQuery] int id)
|
||||
{
|
||||
try
|
||||
{
|
||||
var userId = User.FindFirstValue(ClaimTypes.NameIdentifier);
|
||||
if (userId == null)
|
||||
return Unauthorized(new Response { Status = "Error", Message = "User not authenticated" });
|
||||
|
||||
await _pmScheduleService.DeletePMScheduleAsync(id, userId);
|
||||
return Ok(new DataResponse { Message = "PM Schedule deleted successfully", Status = "200" });
|
||||
}
|
||||
catch (KeyNotFoundException)
|
||||
{
|
||||
return NotFound(new Response { Status = "Error", Message = "PM Schedule not found" });
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return StatusCode(500, new Response { Status = "Error", Message = ex.Message });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,10 +1,12 @@
|
|||
using Api.SeaHavenIndustries.DTOs;
|
||||
using Data.SeaHavenIndustries;
|
||||
using FluentValidation;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using SeaHaven.Services.Interfaces;
|
||||
using SeaHaven.Services.DTOs;
|
||||
using System.Security.Claims;
|
||||
|
||||
namespace Api.SeaHavenIndustries.Controllers
|
||||
{
|
||||
|
|
@ -53,9 +55,13 @@ namespace Api.SeaHavenIndustries.Controllers
|
|||
}
|
||||
|
||||
[HttpGet("{id}")]
|
||||
public async Task<IActionResult> GetVendorById(int id)
|
||||
[HttpGet("GetById")]
|
||||
public async Task<IActionResult> GetVendorById([FromQuery] int? id)
|
||||
{
|
||||
var vendor = await _vendorService.GetVendorByIdAsync(id);
|
||||
if (id == null)
|
||||
return BadRequest(new Response { Status = "Error", Message = "Id is required" });
|
||||
|
||||
var vendor = await _vendorService.GetVendorByIdAsync(id.Value);
|
||||
|
||||
if (vendor == null)
|
||||
return NotFound(new Response { Status = "Error", Message = "Vendor not found" });
|
||||
|
|
@ -64,6 +70,7 @@ namespace Api.SeaHavenIndustries.Controllers
|
|||
}
|
||||
|
||||
[HttpPost]
|
||||
[HttpPost("Create")]
|
||||
public async Task<IActionResult> Create([FromBody] Vendor_DTO model)
|
||||
{
|
||||
try
|
||||
|
|
@ -73,42 +80,88 @@ namespace Api.SeaHavenIndustries.Controllers
|
|||
Name = model.CompanyName ?? throw new ArgumentException("CompanyName is required")
|
||||
};
|
||||
|
||||
var userId = User.Identity?.Name ?? "system";
|
||||
var userId = User.FindFirstValue(ClaimTypes.NameIdentifier);
|
||||
if (userId == null)
|
||||
return Unauthorized(new Response { Status = "Error", Message = "User not authenticated" });
|
||||
|
||||
await _vendorService.CreateVendorAsync(createDto, userId);
|
||||
return Ok(new DataResponse { Message = "Vendor Created", Status = "200" });
|
||||
}
|
||||
catch (ValidationException vex)
|
||||
{
|
||||
var errors = string.Join(", ", vex.Errors.Select(e => e.ErrorMessage));
|
||||
return BadRequest(new Response { Status = "Validation Error", Message = errors });
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return BadRequest(new Response { Status = "Error", Message = ex.Message });
|
||||
return StatusCode(500, new Response { Status = "Error", Message = ex.Message });
|
||||
}
|
||||
}
|
||||
|
||||
[HttpPut("{id}")]
|
||||
public async Task<IActionResult> Update(int id, [FromBody] EditVendor_DTO model)
|
||||
[HttpPost("Update")]
|
||||
public async Task<IActionResult> Update([FromRoute] int? id, [FromBody] EditVendor_DTO model)
|
||||
{
|
||||
try
|
||||
{
|
||||
// For named route, id comes from model
|
||||
int vendorId = id ?? model.Id;
|
||||
if (vendorId == 0)
|
||||
return BadRequest(new Response { Status = "Error", Message = "Vendor Id is required" });
|
||||
|
||||
var updateDto = new UpdateVendorDTO
|
||||
{
|
||||
Name = model.CompanyName
|
||||
};
|
||||
|
||||
var userId = User.Identity?.Name ?? "system";
|
||||
await _vendorService.UpdateVendorAsync(id, updateDto, userId);
|
||||
var userId = User.FindFirstValue(ClaimTypes.NameIdentifier);
|
||||
if (userId == null)
|
||||
return Unauthorized(new Response { Status = "Error", Message = "User not authenticated" });
|
||||
|
||||
await _vendorService.UpdateVendorAsync(vendorId, updateDto, userId);
|
||||
return Ok(new DataResponse { Message = "Vendor Updated", Status = "200" });
|
||||
}
|
||||
catch (ValidationException vex)
|
||||
{
|
||||
var errors = string.Join(", ", vex.Errors.Select(e => e.ErrorMessage));
|
||||
return BadRequest(new Response { Status = "Validation Error", Message = errors });
|
||||
}
|
||||
catch (KeyNotFoundException)
|
||||
{
|
||||
return NotFound(new Response { Status = "Error", Message = "Vendor not found" });
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return BadRequest(new Response { Status = "Error", Message = ex.Message });
|
||||
return StatusCode(500, new Response { Status = "Error", Message = ex.Message });
|
||||
}
|
||||
}
|
||||
|
||||
[HttpDelete("{id}")]
|
||||
public async Task<IActionResult> Delete(int id)
|
||||
[HttpPost("Delete")]
|
||||
public async Task<IActionResult> Delete([FromRoute] int? id, [FromQuery] int? queryId = null)
|
||||
{
|
||||
var userId = User.Identity?.Name ?? "system";
|
||||
await _vendorService.DeleteVendorAsync(id, userId);
|
||||
return Ok(new DataResponse { Message = "Vendor Deleted", Status = "200" });
|
||||
try
|
||||
{
|
||||
// Support both route parameter and query string
|
||||
int vendorId = id ?? queryId ?? 0;
|
||||
if (vendorId == 0)
|
||||
return BadRequest(new Response { Status = "Error", Message = "Vendor Id is required" });
|
||||
|
||||
var userId = User.FindFirstValue(ClaimTypes.NameIdentifier);
|
||||
if (userId == null)
|
||||
return Unauthorized(new Response { Status = "Error", Message = "User not authenticated" });
|
||||
|
||||
await _vendorService.DeleteVendorAsync(vendorId, userId);
|
||||
return Ok(new DataResponse { Message = "Vendor Deleted", Status = "200" });
|
||||
}
|
||||
catch (KeyNotFoundException)
|
||||
{
|
||||
return NotFound(new Response { Status = "Error", Message = "Vendor not found" });
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return StatusCode(500, new Response { Status = "Error", Message = ex.Message });
|
||||
}
|
||||
}
|
||||
|
||||
[HttpGet("Dropdown")]
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -1,4 +1,5 @@
|
|||
using Data.SeaHavenIndustries;
|
||||
using Data.SeaHavenIndustries.Enums;
|
||||
|
||||
namespace Api.SeaHavenIndustries.DTOs
|
||||
{
|
||||
|
|
@ -9,14 +10,11 @@ namespace Api.SeaHavenIndustries.DTOs
|
|||
public string? Description { get; set; }
|
||||
public string? PO { get; set; }
|
||||
public string? TT { get; set; }
|
||||
//public int? ContactId { get; set; }
|
||||
public string? AssignTo { get; set; }
|
||||
public DateTime? DueDate { get; set; }
|
||||
public string? Priority { get; set; }
|
||||
public WorkOrderPriority? Priority { get; set; }
|
||||
public int? LocationId { get; set; }
|
||||
public bool? istemplate { get; set; }
|
||||
//public int? CategoryId { get; set; }
|
||||
|
||||
public List<IFormFile>? Attachments { get; set; }
|
||||
public string? BeforPhotoIssue { get; set; }
|
||||
public IFormFile? BeforPhotoAttachment { get; set; }
|
||||
|
|
@ -25,15 +23,33 @@ namespace Api.SeaHavenIndustries.DTOs
|
|||
public string? SignOffName { get; set; }
|
||||
public IFormFile? SignOffAttachment { get; set; }
|
||||
public string? SignOffSignature { get; set; }
|
||||
public string? WorkOrderContacts { get; set; }
|
||||
public string? WorkOrderCategories { get; set; }
|
||||
public List<int> GetContactIds()
|
||||
public List<int>? ContactIds { get; set; }
|
||||
public List<int>? CategoryIds { get; set; }
|
||||
|
||||
public static WorkOrder MapToEntity(Workorder_DTO dto, string userId)
|
||||
{
|
||||
return WorkOrderContacts?.Split(',').Select(int.Parse).ToList() ?? new List<int>();
|
||||
}
|
||||
public List<int> GetCategoryIds()
|
||||
{
|
||||
return WorkOrderCategories?.Split(',').Select(int.Parse).ToList() ?? new List<int>();
|
||||
if (dto == null) return null!;
|
||||
|
||||
return new WorkOrder
|
||||
{
|
||||
WorkerOrderNumber = dto.WorkerOrderNumber,
|
||||
WorkerOrderTitle = dto.WorkerOrderTitle,
|
||||
Description = dto.Description,
|
||||
PO = dto.PO,
|
||||
TT = dto.TT,
|
||||
AssignTo = dto.AssignTo,
|
||||
DueDate = dto.DueDate,
|
||||
Priority = (dto.Priority ?? WorkOrderPriority.Medium).ToString(),
|
||||
LocationId = dto.LocationId,
|
||||
istemplate = dto.istemplate ?? false,
|
||||
Status = WorkOrderStatus.Open.ToString(),
|
||||
BeforPhotoIssue = dto.BeforPhotoIssue,
|
||||
AfterPhotoIssue = dto.AfterPhotoIssue,
|
||||
SignOffName = dto.SignOffName,
|
||||
SignOffSignature = dto.SignOffSignature,
|
||||
createdby = userId,
|
||||
CreatedDate = DateTime.UtcNow
|
||||
};
|
||||
}
|
||||
}
|
||||
public class EditWorkorder_DTO
|
||||
|
|
@ -46,8 +62,8 @@ namespace Api.SeaHavenIndustries.DTOs
|
|||
public string? TT { get; set; }
|
||||
public string? AssignTo { get; set; }
|
||||
public DateTime? DueDate { get; set; }
|
||||
public string? Priority { get; set; }
|
||||
public string? Status { get; set; }
|
||||
public WorkOrderPriority? Priority { get; set; }
|
||||
public WorkOrderStatus? Status { get; set; }
|
||||
public int? LocationId { get; set; }
|
||||
public string? Problem { get; set; }
|
||||
public string? Trade { get; set; }
|
||||
|
|
@ -56,7 +72,6 @@ namespace Api.SeaHavenIndustries.DTOs
|
|||
public DateTime? ScheduledDate { get; set; }
|
||||
public DateTime? CompletedDate { get; set; }
|
||||
public string? Source { get; set; }
|
||||
|
||||
public List<IFormFile>? Attachments { get; set; }
|
||||
public string? BeforPhotoIssue { get; set; }
|
||||
public IFormFile? BeforPhotoAttachment { get; set; }
|
||||
|
|
@ -65,16 +80,36 @@ namespace Api.SeaHavenIndustries.DTOs
|
|||
public string? SignOffName { get; set; }
|
||||
public IFormFile? SignOffAttachment { get; set; }
|
||||
public string? SignOffSignature { get; set; }
|
||||
public string? WorkOrderContacts { get; set; }
|
||||
public string? WorkOrderCategories { get; set; }
|
||||
public List<int> GetContactIds()
|
||||
public List<int>? ContactIds { get; set; }
|
||||
public List<int>? CategoryIds { get; set; }
|
||||
|
||||
public static void UpdateEntity(EditWorkorder_DTO dto, WorkOrder entity)
|
||||
{
|
||||
return WorkOrderContacts?.Split(',').Select(int.Parse).ToList() ?? new List<int>();
|
||||
}
|
||||
public List<int> GetCategoryIds()
|
||||
{
|
||||
return WorkOrderCategories?.Split(',').Select(int.Parse).ToList() ?? new List<int>();
|
||||
if (dto == null || entity == null) return;
|
||||
|
||||
if (dto.WorkerOrderNumber != null) entity.WorkerOrderNumber = dto.WorkerOrderNumber;
|
||||
if (dto.WorkerOrderTitle != null) entity.WorkerOrderTitle = dto.WorkerOrderTitle;
|
||||
if (dto.Description != null) entity.Description = dto.Description;
|
||||
if (dto.PO != null) entity.PO = dto.PO;
|
||||
if (dto.TT != null) entity.TT = dto.TT;
|
||||
if (dto.AssignTo != null) entity.AssignTo = dto.AssignTo;
|
||||
if (dto.DueDate.HasValue) entity.DueDate = dto.DueDate;
|
||||
if (dto.Priority.HasValue) entity.Priority = dto.Priority.ToString()!;
|
||||
if (dto.Status.HasValue) entity.Status = dto.Status.ToString()!;
|
||||
if (dto.LocationId.HasValue) entity.LocationId = dto.LocationId;
|
||||
if (dto.Problem != null) entity.Problem = dto.Problem;
|
||||
if (dto.Trade != null) entity.Trade = dto.Trade;
|
||||
if (dto.SubTrade != null) entity.SubTrade = dto.SubTrade;
|
||||
if (dto.VendorNTE.HasValue) entity.VendorNTE = dto.VendorNTE;
|
||||
if (dto.ScheduledDate.HasValue) entity.ScheduledDate = dto.ScheduledDate;
|
||||
if (dto.CompletedDate.HasValue) entity.CompletedDate = dto.CompletedDate;
|
||||
if (dto.Source != null) entity.Source = dto.Source;
|
||||
if (dto.BeforPhotoIssue != null) entity.BeforPhotoIssue = dto.BeforPhotoIssue;
|
||||
if (dto.AfterPhotoIssue != null) entity.AfterPhotoIssue = dto.AfterPhotoIssue;
|
||||
if (dto.SignOffName != null) entity.SignOffName = dto.SignOffName;
|
||||
if (dto.SignOffSignature != null) entity.SignOffSignature = dto.SignOffSignature;
|
||||
|
||||
entity.LastModificationTime = DateTime.UtcNow;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -7,6 +7,8 @@ using Microsoft.EntityFrameworkCore;
|
|||
using Microsoft.IdentityModel.Tokens;
|
||||
using Microsoft.OpenApi.Models;
|
||||
using System.Text;
|
||||
using SeaHaven.DataServices.DependencyInjection;
|
||||
using SeaHaven.Services.DependencyInjection;
|
||||
|
||||
var builder = WebApplication.CreateBuilder(args);
|
||||
ConfigurationManager configuration = builder.Configuration;
|
||||
|
|
@ -31,7 +33,7 @@ builder.Services.AddResponseCompression(opts =>
|
|||
});
|
||||
builder.Services.AddCors(option =>
|
||||
option.AddDefaultPolicy(builder => builder.AllowAnyOrigin().AllowAnyHeader().AllowAnyMethod()));
|
||||
// Helper services
|
||||
|
||||
builder.Services.AddScoped<SendMessage>();
|
||||
builder.Services.AddScoped<Api.SeaHavenIndustries.Helper.VendorPortalTokenService>();
|
||||
builder.Services.AddSingleton<Api.SeaHavenIndustries.Helper.ZipCodeDistance>(sp =>
|
||||
|
|
@ -43,36 +45,8 @@ builder.Services.AddSingleton<Api.SeaHavenIndustries.Helper.ZipCodeDistance>(sp
|
|||
return zipService;
|
||||
});
|
||||
|
||||
// ========================================================================
|
||||
// MULTI-TIER ARCHITECTURE - Dependency Injection Registration
|
||||
// ========================================================================
|
||||
|
||||
// Data Services (Data Access Layer)
|
||||
builder.Services.AddScoped<SeaHaven.DataServices.Interfaces.IContactDataService, SeaHaven.DataServices.Implementation.ContactDataService>();
|
||||
builder.Services.AddScoped<SeaHaven.DataServices.Interfaces.IWorkOrderDataService, SeaHaven.DataServices.Implementation.WorkOrderDataService>();
|
||||
builder.Services.AddScoped<SeaHaven.DataServices.Interfaces.ILocationDataService, SeaHaven.DataServices.Implementation.LocationDataService>();
|
||||
builder.Services.AddScoped<SeaHaven.DataServices.Interfaces.IEmployeeDataService, SeaHaven.DataServices.Implementation.EmployeeDataService>();
|
||||
builder.Services.AddScoped<SeaHaven.DataServices.Interfaces.IAccountDataService, SeaHaven.DataServices.Implementation.AccountDataService>();
|
||||
builder.Services.AddScoped<SeaHaven.DataServices.Interfaces.IVendorDataService, SeaHaven.DataServices.Implementation.VendorDataService>();
|
||||
builder.Services.AddScoped<SeaHaven.DataServices.Interfaces.IDispatchDataService, SeaHaven.DataServices.Implementation.DispatchDataService>();
|
||||
builder.Services.AddScoped<SeaHaven.DataServices.Interfaces.IFollowUpDataService, SeaHaven.DataServices.Implementation.FollowUpDataService>();
|
||||
builder.Services.AddScoped<SeaHaven.DataServices.Interfaces.IPMScheduleDataService, SeaHaven.DataServices.Implementation.PMScheduleDataService>();
|
||||
builder.Services.AddScoped<SeaHaven.DataServices.Interfaces.IAssetDataService, SeaHaven.DataServices.Implementation.AssetDataService>();
|
||||
builder.Services.AddScoped<SeaHaven.DataServices.Interfaces.ICategoryDataService, SeaHaven.DataServices.Implementation.CategoryDataService>();
|
||||
|
||||
// Business Services (Business Logic Layer)
|
||||
builder.Services.AddScoped<SeaHaven.Services.Interfaces.IContactService, SeaHaven.Services.Implementation.ContactService>();
|
||||
builder.Services.AddScoped<SeaHaven.Services.Interfaces.IWorkOrderService, SeaHaven.Services.Implementation.WorkOrderService>();
|
||||
builder.Services.AddScoped<SeaHaven.Services.Interfaces.ILocationService, SeaHaven.Services.Implementation.LocationService>();
|
||||
builder.Services.AddScoped<SeaHaven.Services.Interfaces.IEmployeeService, SeaHaven.Services.Implementation.EmployeeService>();
|
||||
builder.Services.AddScoped<SeaHaven.Services.Interfaces.IAccountService, SeaHaven.Services.Implementation.AccountService>();
|
||||
builder.Services.AddScoped<SeaHaven.Services.Interfaces.IVendorService, SeaHaven.Services.Implementation.VendorService>();
|
||||
builder.Services.AddScoped<SeaHaven.Services.Interfaces.IDispatchService, SeaHaven.Services.Implementation.DispatchService>();
|
||||
builder.Services.AddScoped<SeaHaven.Services.Interfaces.IFollowUpService, SeaHaven.Services.Implementation.FollowUpService>();
|
||||
builder.Services.AddScoped<SeaHaven.Services.Interfaces.IPMScheduleService, SeaHaven.Services.Implementation.PMScheduleService>();
|
||||
builder.Services.AddScoped<SeaHaven.Services.Interfaces.IAssetService, SeaHaven.Services.Implementation.AssetService>();
|
||||
|
||||
// ========================================================================
|
||||
builder.Services.AddDataServices();
|
||||
builder.Services.AddBusinessServices();
|
||||
|
||||
builder.Services.AddAuthentication(options =>
|
||||
{
|
||||
|
|
|
|||
531
BACKEND_ARCHITECTURE.md
Normal file
531
BACKEND_ARCHITECTURE.md
Normal file
|
|
@ -0,0 +1,531 @@
|
|||
# Backend Architecture Guide
|
||||
|
||||
## 🏗️ Clean Architecture Overview
|
||||
|
||||
The backend follows **Clean Architecture** with strict layer separation:
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────────┐
|
||||
│ API LAYER (Controllers) │
|
||||
│ - HTTP Request/Response │
|
||||
│ - Authentication & Authorization │
|
||||
│ - Exception Handling │
|
||||
│ - Input Validation (basic) │
|
||||
└─────────────────┬───────────────────────────────────────────┘
|
||||
│ Calls
|
||||
▼
|
||||
┌─────────────────────────────────────────────────────────────┐
|
||||
│ SERVICE LAYER (Business Logic) │
|
||||
│ - Business Rules │
|
||||
│ - FluentValidation (detailed validation) │
|
||||
│ - Data Transformation │
|
||||
│ - Orchestration │
|
||||
└─────────────────┬───────────────────────────────────────────┘
|
||||
│ Calls
|
||||
▼
|
||||
┌─────────────────────────────────────────────────────────────┐
|
||||
│ DATA SERVICE LAYER (Persistence) │
|
||||
│ - Database Access │
|
||||
│ - Entity Operations (CRUD) │
|
||||
│ - Query Building │
|
||||
│ - Transaction Management │
|
||||
└─────────────────┬───────────────────────────────────────────┘
|
||||
│ Uses
|
||||
▼
|
||||
┌─────────────────────────────────────────────────────────────┐
|
||||
│ DATA LAYER (EF Core) │
|
||||
│ - DbContext │
|
||||
│ - Entity Models │
|
||||
│ - Database Schema │
|
||||
└─────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 📁 Project Structure
|
||||
|
||||
```
|
||||
backend/shoc-backend/
|
||||
│
|
||||
├── Api.SeaHavenIndustries/ # 🌐 API Layer
|
||||
│ ├── Controllers/ # HTTP endpoints
|
||||
│ ├── DTOs/ # Data Transfer Objects (API contracts)
|
||||
│ ├── Program.cs # App startup & DI registration
|
||||
│ └── appsettings.json # Configuration
|
||||
│
|
||||
├── SeaHaven.Services/ # 💼 Service Layer (Business Logic)
|
||||
│ ├── Interfaces/ # Service contracts (IContactService, etc.)
|
||||
│ ├── Implementation/ # Service implementations
|
||||
│ ├── DTOs/ # Internal DTOs for service layer
|
||||
│ └── Validators/ # FluentValidation validators
|
||||
│
|
||||
├── SeaHaven.DataServices/ # 💾 Data Access Layer
|
||||
│ ├── Interfaces/ # DataService contracts (IContactDataService, etc.)
|
||||
│ ├── Implementation/ # DataService implementations
|
||||
│ └── DTOs/ # Data layer DTOs
|
||||
│
|
||||
└── Data.SeaHavenIndustries/ # 🗄️ Data Layer (EF Core)
|
||||
├── Models/ # Entity models (Contact, Employee, etc.)
|
||||
├── ApplicationDbContext.cs # EF Core DbContext
|
||||
└── Migrations/ # Database migrations
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🔄 Data Flow Example (Creating a Contact)
|
||||
|
||||
### 1️⃣ **API Layer** (`ContactController.cs`)
|
||||
```csharp
|
||||
[HttpPost("Create")]
|
||||
public async Task<IActionResult> AddContacts(Contact_DTO dto)
|
||||
{
|
||||
try
|
||||
{
|
||||
// Get authenticated user ID
|
||||
var userId = _userManager.GetUserId(User);
|
||||
if (userId == null)
|
||||
return Unauthorized(new Response { Status = "Error", Message = "User not authenticated" });
|
||||
|
||||
// Convert API DTO → Service DTO
|
||||
var serviceDto = dto.ToServiceCreateDTO();
|
||||
|
||||
// Call Service Layer
|
||||
await _contactService.CreateContactAsync(serviceDto, userId);
|
||||
|
||||
return Ok(new Response { Status = "Success", Message = "Contact created" });
|
||||
}
|
||||
catch (ValidationException vex)
|
||||
{
|
||||
var errors = string.Join(", ", vex.Errors.Select(e => e.ErrorMessage));
|
||||
return BadRequest(new Response { Status = "Validation Error", Message = errors });
|
||||
}
|
||||
catch (KeyNotFoundException)
|
||||
{
|
||||
return NotFound(new Response { Status = "Error", Message = "Resource not found" });
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return StatusCode(500, new Response { Status = "Error", Message = ex.Message });
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Responsibilities:**
|
||||
- ✅ Authenticate user
|
||||
- ✅ Extract user ID from claims
|
||||
- ✅ Map API DTO to Service DTO
|
||||
- ✅ Catch exceptions and return proper HTTP status codes
|
||||
- ❌ NO business logic
|
||||
- ❌ NO database access
|
||||
|
||||
---
|
||||
|
||||
### 2️⃣ **Service Layer** (`ContactService.cs`)
|
||||
|
||||
```csharp
|
||||
public class ContactService : IContactService
|
||||
{
|
||||
private readonly IContactDataService _dataService;
|
||||
private readonly IValidator<CreateContactDTO> _createValidator;
|
||||
|
||||
public ContactService(
|
||||
IContactDataService dataService,
|
||||
IValidator<CreateContactDTO> createValidator)
|
||||
{
|
||||
_dataService = dataService;
|
||||
_createValidator = createValidator;
|
||||
}
|
||||
|
||||
public async Task<ContactDTO> CreateContactAsync(CreateContactDTO dto, string userId)
|
||||
{
|
||||
// STEP 1: Validate using FluentValidation
|
||||
var validationResult = await _createValidator.ValidateAsync(dto);
|
||||
if (!validationResult.IsValid)
|
||||
{
|
||||
throw new ValidationException(validationResult.Errors);
|
||||
}
|
||||
|
||||
// STEP 2: Business logic (if any)
|
||||
// Example: Check for duplicates, apply business rules, etc.
|
||||
|
||||
// STEP 3: Map Service DTO → Data DTO
|
||||
var dataDto = new CreateContactDataDTO
|
||||
{
|
||||
FirstName = dto.FirstName,
|
||||
LastName = dto.LastName,
|
||||
Email = dto.Email,
|
||||
Phone = dto.Phone
|
||||
};
|
||||
|
||||
// STEP 4: Call Data Service Layer
|
||||
var createdContact = await _dataService.CreateAsync(dataDto, userId);
|
||||
|
||||
// STEP 5: Map Data DTO → Service DTO and return
|
||||
return new ContactDTO
|
||||
{
|
||||
Id = createdContact.Id,
|
||||
FirstName = createdContact.FirstName,
|
||||
LastName = createdContact.LastName,
|
||||
Email = createdContact.Email,
|
||||
Phone = createdContact.Phone
|
||||
};
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Responsibilities:**
|
||||
- ✅ Validate input using FluentValidation
|
||||
- ✅ Apply business rules
|
||||
- ✅ Orchestrate operations
|
||||
- ✅ Transform data between layers
|
||||
- ❌ NO HTTP concerns
|
||||
- ❌ NO direct database access
|
||||
|
||||
---
|
||||
|
||||
### 3️⃣ **Data Service Layer** (`ContactDataService.cs`)
|
||||
|
||||
```csharp
|
||||
public class ContactDataService : IContactDataService
|
||||
{
|
||||
private readonly ApplicationDbContext _db;
|
||||
|
||||
public ContactDataService(ApplicationDbContext db)
|
||||
{
|
||||
_db = db;
|
||||
}
|
||||
|
||||
public async Task<ContactDataDTO> CreateAsync(CreateContactDataDTO dto, string userId)
|
||||
{
|
||||
// STEP 1: Create entity from DTO
|
||||
var contact = new Contact
|
||||
{
|
||||
FirstName = dto.FirstName,
|
||||
LastName = dto.LastName,
|
||||
Email = dto.Email,
|
||||
Phone = dto.Phone,
|
||||
CreatedBy = userId,
|
||||
CreatedDate = DateTime.UtcNow,
|
||||
ModifiedBy = userId,
|
||||
ModifiedDate = DateTime.UtcNow
|
||||
};
|
||||
|
||||
// STEP 2: Add to database
|
||||
_db.Contacts.Add(contact);
|
||||
await _db.SaveChangesAsync();
|
||||
|
||||
// STEP 3: Map entity → Data DTO and return
|
||||
return new ContactDataDTO
|
||||
{
|
||||
Id = contact.Id,
|
||||
FirstName = contact.FirstName,
|
||||
LastName = contact.LastName,
|
||||
Email = contact.Email,
|
||||
Phone = contact.Phone
|
||||
};
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Responsibilities:**
|
||||
- ✅ Direct database operations (CRUD)
|
||||
- ✅ Entity mapping
|
||||
- ✅ Set audit fields (CreatedBy, CreatedDate, etc.)
|
||||
- ✅ Transaction management
|
||||
- ❌ NO validation
|
||||
- ❌ NO business logic
|
||||
|
||||
---
|
||||
|
||||
## 🔌 Dependency Injection (DI)
|
||||
|
||||
### Automatic Assembly Scanning
|
||||
|
||||
Services are **automatically registered** by scanning assemblies using naming convention.
|
||||
|
||||
**In `Program.cs`:**
|
||||
```csharp
|
||||
builder.Services.AddDataServices(); // Auto-register all Data Services
|
||||
builder.Services.AddBusinessServices(); // Auto-register all Business Services + Validators
|
||||
```
|
||||
|
||||
### How It Works
|
||||
|
||||
**Module Files:**
|
||||
- `SeaHaven.DataServices/DependencyInjection/DataServicesModule.cs`
|
||||
- `SeaHaven.Services/DependencyInjection/ServicesModule.cs`
|
||||
|
||||
**Process:**
|
||||
1. Scans assembly for all classes
|
||||
2. Finds interface matching pattern `I{ClassName}`
|
||||
3. Registers as Scoped service
|
||||
4. Also auto-registers FluentValidation validators
|
||||
|
||||
### Naming Convention Required
|
||||
|
||||
| Interface | Implementation | Registered? |
|
||||
|-----------|---------------|-------------|
|
||||
| `IContactService` | `ContactService` | ✅ YES |
|
||||
| `IContactDataService` | `ContactDataService` | ✅ YES |
|
||||
| `ISomething` | `SomethingDifferent` | ❌ NO |
|
||||
|
||||
**Rule**: Interface must be `I` + Class name exactly
|
||||
|
||||
### Usage in Code
|
||||
|
||||
Once registered, inject via constructor:
|
||||
|
||||
```csharp
|
||||
public class ContactController : Controller
|
||||
{
|
||||
private readonly IContactService _contactService;
|
||||
|
||||
public ContactController(IContactService contactService)
|
||||
{
|
||||
_contactService = contactService;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Benefits
|
||||
|
||||
✅ No manual registration - just follow naming convention
|
||||
✅ Auto-discovery - new services work automatically
|
||||
✅ FluentValidation auto-registered
|
||||
|
||||
---
|
||||
|
||||
## ✅ FluentValidation
|
||||
|
||||
### Validator Example (`CreateContactDTOValidator.cs`)
|
||||
|
||||
```csharp
|
||||
public class CreateContactDTOValidator : AbstractValidator<CreateContactDTO>
|
||||
{
|
||||
public CreateContactDTOValidator()
|
||||
{
|
||||
RuleFor(x => x.FirstName)
|
||||
.NotEmpty().WithMessage("First name is required")
|
||||
.MaximumLength(50).WithMessage("First name cannot exceed 50 characters");
|
||||
|
||||
RuleFor(x => x.LastName)
|
||||
.NotEmpty().WithMessage("Last name is required")
|
||||
.MaximumLength(50).WithMessage("Last name cannot exceed 50 characters");
|
||||
|
||||
RuleFor(x => x.Email)
|
||||
.NotEmpty().WithMessage("Email is required")
|
||||
.EmailAddress().WithMessage("Invalid email format")
|
||||
.MaximumLength(100).WithMessage("Email cannot exceed 100 characters");
|
||||
|
||||
RuleFor(x => x.Phone)
|
||||
.Matches(@"^\+?[1-9]\d{1,14}$").When(x => !string.IsNullOrEmpty(x.Phone))
|
||||
.WithMessage("Invalid phone number format");
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Where Validation Happens
|
||||
|
||||
```
|
||||
❌ Controller → NO detailed validation (only catches ValidationException)
|
||||
✅ Service → YES - FluentValidation runs here
|
||||
❌ DataService → NO validation
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🎯 Interface Examples
|
||||
|
||||
### Service Interface (`IContactService.cs`)
|
||||
```csharp
|
||||
public interface IContactService
|
||||
{
|
||||
Task<ContactDTO> CreateContactAsync(CreateContactDTO dto, string userId);
|
||||
Task<ContactDTO> UpdateContactAsync(int id, UpdateContactDTO dto, string userId);
|
||||
Task DeleteContactAsync(int id, string userId);
|
||||
Task<ContactDTO> GetContactByIdAsync(int id);
|
||||
Task<PagedResult<ContactDTO>> GetContactsPagedAsync(int page, int pageSize, string? search);
|
||||
}
|
||||
```
|
||||
|
||||
### Data Service Interface (`IContactDataService.cs`)
|
||||
```csharp
|
||||
public interface IContactDataService
|
||||
{
|
||||
Task<ContactDataDTO> CreateAsync(CreateContactDataDTO dto, string userId);
|
||||
Task<ContactDataDTO> UpdateAsync(int id, UpdateContactDataDTO dto, string userId);
|
||||
Task DeleteAsync(int id, string userId);
|
||||
Task<ContactDataDTO?> GetByIdAsync(int id);
|
||||
Task<PagedResult<ContactDataDTO>> GetPagedAsync(int page, int pageSize, string? search);
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 📦 DTO Types
|
||||
|
||||
### 1. **API DTOs** (`Api.SeaHavenIndustries/DTOs/`)
|
||||
- Used for HTTP requests/responses
|
||||
- Map to frontend JSON structure
|
||||
- Example: `Contact_DTO`, `EditContact_DTO`
|
||||
|
||||
### 2. **Service DTOs** (`SeaHaven.Services/DTOs/`)
|
||||
- Used between Controller ↔ Service
|
||||
- Business-focused structure
|
||||
- Example: `CreateContactDTO`, `UpdateContactDTO`, `ContactDTO`
|
||||
|
||||
### 3. **Data DTOs** (`SeaHaven.DataServices/DTOs/`)
|
||||
- Used between Service ↔ DataService
|
||||
- Database-focused structure
|
||||
- Example: `CreateContactDataDTO`, `ContactDataDTO`
|
||||
|
||||
### DTO Mapping Flow
|
||||
```
|
||||
API DTO → Service DTO → Data DTO → Entity
|
||||
(HTTP) (Business) (Data) (Database)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🚨 Exception Handling Pattern
|
||||
|
||||
### Controller Exception Handling (STANDARD PATTERN)
|
||||
|
||||
```csharp
|
||||
[HttpPost("Create")]
|
||||
public async Task<IActionResult> Create([FromBody] SomeDTO dto)
|
||||
{
|
||||
try
|
||||
{
|
||||
// Get user ID
|
||||
var userId = User.FindFirstValue(ClaimTypes.NameIdentifier);
|
||||
if (userId == null)
|
||||
return Unauthorized(new Response { Status = "Error", Message = "User not authenticated" });
|
||||
|
||||
// Call service
|
||||
await _service.CreateAsync(dto, userId);
|
||||
|
||||
return Ok(new Response { Status = "Success", Message = "Created successfully" });
|
||||
}
|
||||
catch (ValidationException vex) // ⚠️ CATCH THIS FIRST
|
||||
{
|
||||
var errors = string.Join(", ", vex.Errors.Select(e => e.ErrorMessage));
|
||||
return BadRequest(new Response { Status = "Validation Error", Message = errors });
|
||||
}
|
||||
catch (KeyNotFoundException) // ⚠️ THEN THIS
|
||||
{
|
||||
return NotFound(new Response { Status = "Error", Message = "Not found" });
|
||||
}
|
||||
catch (Exception ex) // ⚠️ GENERIC LAST
|
||||
{
|
||||
return StatusCode(500, new Response { Status = "Error", Message = ex.Message });
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### HTTP Status Code Mapping
|
||||
- **200 OK** → Success
|
||||
- **400 Bad Request** → Validation errors
|
||||
- **401 Unauthorized** → Not authenticated
|
||||
- **404 Not Found** → Resource not found
|
||||
- **500 Internal Server Error** → Unexpected errors
|
||||
|
||||
---
|
||||
|
||||
## 🔐 Authentication & User ID
|
||||
|
||||
### Getting User ID (Two Methods)
|
||||
|
||||
**Method 1: Using Claims (Preferred)**
|
||||
```csharp
|
||||
var userId = User.FindFirstValue(ClaimTypes.NameIdentifier);
|
||||
if (userId == null)
|
||||
return Unauthorized(new Response { Status = "Error", Message = "User not authenticated" });
|
||||
```
|
||||
|
||||
**Method 2: Using UserManager (for Identity)**
|
||||
```csharp
|
||||
var userId = _userManager.GetUserId(User);
|
||||
if (userId == null)
|
||||
return Unauthorized(new Response { Status = "Error", Message = "User not authenticated" });
|
||||
```
|
||||
|
||||
### Audit Fields
|
||||
Every entity should track:
|
||||
- `CreatedBy` (string - User ID GUID)
|
||||
- `CreatedDate` (DateTime)
|
||||
- `ModifiedBy` (string - User ID GUID)
|
||||
- `ModifiedDate` (DateTime)
|
||||
|
||||
---
|
||||
|
||||
## 📋 Naming Conventions
|
||||
|
||||
### Controllers
|
||||
- Named after entity + "Controller"
|
||||
- Example: `ContactController`, `EmployeeController`
|
||||
|
||||
### Services
|
||||
- Interface: `I[Entity]Service`
|
||||
- Implementation: `[Entity]Service`
|
||||
- Example: `IContactService`, `ContactService`
|
||||
|
||||
### Data Services
|
||||
- Interface: `I[Entity]DataService`
|
||||
- Implementation: `[Entity]DataService`
|
||||
- Example: `IContactDataService`, `ContactDataService`
|
||||
|
||||
### DTOs
|
||||
- API Layer: `[Entity]_DTO`, `Edit[Entity]_DTO`
|
||||
- Service Layer: `Create[Entity]DTO`, `Update[Entity]DTO`, `[Entity]DTO`
|
||||
- Data Layer: `Create[Entity]DataDTO`, `[Entity]DataDTO`
|
||||
|
||||
---
|
||||
|
||||
## ✅ Best Practices Checklist
|
||||
|
||||
### Controllers
|
||||
- [ ] Use dependency injection for services
|
||||
- [ ] Get user ID from `User.FindFirstValue(ClaimTypes.NameIdentifier)`
|
||||
- [ ] Return 401 if userId is null
|
||||
- [ ] Catch `ValidationException` BEFORE generic `Exception`
|
||||
- [ ] Return appropriate HTTP status codes
|
||||
- [ ] NO business logic in controllers
|
||||
- [ ] NO direct database access (`_db`)
|
||||
|
||||
### Services
|
||||
- [ ] Validate using FluentValidation
|
||||
- [ ] Throw `ValidationException` for validation errors
|
||||
- [ ] Throw `KeyNotFoundException` for not found
|
||||
- [ ] Pass userId to DataService
|
||||
- [ ] Transform DTOs between layers
|
||||
- [ ] Implement business rules here
|
||||
|
||||
### Data Services
|
||||
- [ ] Direct database access only
|
||||
- [ ] Set audit fields (CreatedBy, CreatedDate, ModifiedBy, ModifiedDate)
|
||||
- [ ] Use async/await for all database operations
|
||||
- [ ] NO validation logic
|
||||
- [ ] NO business logic
|
||||
|
||||
---
|
||||
|
||||
## 🎓 Quick Reference
|
||||
|
||||
| Layer | Purpose | Can Access | Cannot Access |
|
||||
|-------|---------|-----------|---------------|
|
||||
| **Controller** | HTTP handling, auth | Service layer | Database directly |
|
||||
| **Service** | Business logic, validation | DataService layer | Database directly |
|
||||
| **DataService** | Database operations | DbContext, Entities | HTTP context |
|
||||
| **Data** | Entity definitions | Nothing (POCOs) | Application logic |
|
||||
|
||||
---
|
||||
|
||||
## 📚 Example: Full CRUD Implementation
|
||||
|
||||
See these files for complete examples:
|
||||
- **Contact**: `ContactController.cs`, `ContactService.cs`, `ContactDataService.cs`
|
||||
- **Employee**: `EmployeeController.cs`, `EmployeeService.cs`, `EmployeeDataService.cs`
|
||||
- **Asset**: `AssetController.cs`, `AssetService.cs`, `AssetDataService.cs`
|
||||
|
||||
All follow the same Clean Architecture pattern!
|
||||
|
|
@ -1,553 +0,0 @@
|
|||
/*
|
||||
===================================================================================
|
||||
CALENDAR/EVENTS MODULE - COMPLETE IMPLEMENTATION
|
||||
===================================================================================
|
||||
This file contains everything needed for the Calendar/Events module:
|
||||
1. Events Model (Database Entity)
|
||||
2. Event_DTO (for GET responses)
|
||||
3. EditEvent_DTO (for POST/PUT requests)
|
||||
4. CalendarController (API endpoints)
|
||||
5. SQL Script for database table creation and sample data
|
||||
|
||||
Author: AI Agent
|
||||
Date: 2026-03-30
|
||||
===================================================================================
|
||||
*/
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Api.SeaHavenIndustries.DTOs;
|
||||
using Data.SeaHavenIndustries;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Identity;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
|
||||
// ===================================================================================
|
||||
// SECTION 1: DATABASE MODEL (Data.SeaHavenIndustries/Models/Events.cs)
|
||||
// ===================================================================================
|
||||
|
||||
namespace Data.SeaHavenIndustries
|
||||
{
|
||||
public class Events : FullAuditEntity
|
||||
{
|
||||
public string Title { get; set; } = null!;
|
||||
public string? Description { get; set; }
|
||||
public string? Location { get; set; }
|
||||
|
||||
// Related Record (can link to Accounts, Contacts, Work Orders, etc.)
|
||||
public int? RecordID { get; set; }
|
||||
public string? RecordName { get; set; }
|
||||
public string? RecordTargetType { get; set; }
|
||||
|
||||
// Reminder Settings
|
||||
public int ReminderMinutes { get; set; } = 15; // Default: 15 minutes before
|
||||
|
||||
// Color for calendar display
|
||||
public string EventColor { get; set; } = "primary"; // primary, success, info, warning, danger
|
||||
|
||||
// Schedule
|
||||
public DateTime StartDate { get; set; }
|
||||
public string? StartTime { get; set; } // e.g., "08:00 AM"
|
||||
public DateTime EndDate { get; set; }
|
||||
public string? EndTime { get; set; } // e.g., "08:30 AM"
|
||||
public bool AllDay { get; set; } = false;
|
||||
}
|
||||
}
|
||||
|
||||
// ===================================================================================
|
||||
// SECTION 2: DTOs (Api.SeaHavenIndustries/DTOs/Event_DTO.cs)
|
||||
// ===================================================================================
|
||||
|
||||
namespace Api.SeaHavenIndustries.DTOs
|
||||
{
|
||||
// DTO for GET responses
|
||||
public class Event_DTO
|
||||
{
|
||||
public int Id { get; set; }
|
||||
public string Title { get; set; } = null!;
|
||||
public string? Description { get; set; }
|
||||
public string? Location { get; set; }
|
||||
|
||||
public int? RecordID { get; set; }
|
||||
public string? RecordName { get; set; }
|
||||
public string? RecordTargetType { get; set; }
|
||||
|
||||
public int ReminderMinutes { get; set; }
|
||||
public string EventColor { get; set; } = null!;
|
||||
|
||||
public DateTime StartDate { get; set; }
|
||||
public string? StartTime { get; set; }
|
||||
public DateTime EndDate { get; set; }
|
||||
public string? EndTime { get; set; }
|
||||
public bool AllDay { get; set; }
|
||||
|
||||
public DateTime? CreatedDate { get; set; }
|
||||
public string? createdby { get; set; }
|
||||
}
|
||||
|
||||
// DTO for POST/PUT requests (Create/Edit)
|
||||
public class EditEvent_DTO
|
||||
{
|
||||
public int? Id { get; set; } // Null for create, populated for edit
|
||||
|
||||
[Required(ErrorMessage = "Please enter a title")]
|
||||
[StringLength(100, ErrorMessage = "Title cannot be longer than 100 characters")]
|
||||
public string Title { get; set; } = null!;
|
||||
|
||||
public string? Description { get; set; }
|
||||
|
||||
[StringLength(150, ErrorMessage = "Location cannot be longer than 150 characters")]
|
||||
public string? Location { get; set; }
|
||||
|
||||
public int? RecordID { get; set; }
|
||||
public string? RecordName { get; set; }
|
||||
public string? RecordTargetType { get; set; }
|
||||
|
||||
[Required(ErrorMessage = "The Remind Attendees field is required")]
|
||||
public int ReminderMinutes { get; set; } = 15;
|
||||
|
||||
[Required(ErrorMessage = "The Select a Calendar Color field is required")]
|
||||
public string EventColor { get; set; } = "primary";
|
||||
|
||||
[Required(ErrorMessage = "Please select a start date")]
|
||||
public DateTime StartDate { get; set; }
|
||||
|
||||
[Required(ErrorMessage = "Please select a start time")]
|
||||
public string StartTime { get; set; } = null!;
|
||||
|
||||
[Required(ErrorMessage = "Please select an end date")]
|
||||
public DateTime EndDate { get; set; }
|
||||
|
||||
[Required(ErrorMessage = "Please select an end time")]
|
||||
public string EndTime { get; set; } = null!;
|
||||
|
||||
public bool AllDay { get; set; } = false;
|
||||
}
|
||||
}
|
||||
|
||||
// ===================================================================================
|
||||
// SECTION 3: CONTROLLER (Api.SeaHavenIndustries/Controllers/CalendarController.cs)
|
||||
// ===================================================================================
|
||||
|
||||
namespace Api.SeaHavenIndustries.Controllers
|
||||
{
|
||||
//[Authorize] // Uncomment when authentication is needed
|
||||
[ApiController]
|
||||
[Route("api/Calendar")]
|
||||
public class CalendarController : Controller
|
||||
{
|
||||
private readonly UserManager<ApplicationUser> _userManager;
|
||||
private readonly ApplicationDbContext _db;
|
||||
|
||||
public CalendarController(UserManager<ApplicationUser> userManager, ApplicationDbContext db)
|
||||
{
|
||||
_userManager = userManager;
|
||||
_db = db;
|
||||
}
|
||||
|
||||
// ===================================================================================
|
||||
// CREATE EVENT
|
||||
// ===================================================================================
|
||||
[HttpPost]
|
||||
[Route("AddEvent")]
|
||||
public async Task<IActionResult> AddEvent(EditEvent_DTO eventDto)
|
||||
{
|
||||
try
|
||||
{
|
||||
var model = new Events
|
||||
{
|
||||
Title = eventDto.Title,
|
||||
Description = eventDto.Description,
|
||||
Location = eventDto.Location,
|
||||
RecordID = eventDto.RecordID,
|
||||
RecordName = eventDto.RecordName,
|
||||
RecordTargetType = eventDto.RecordTargetType,
|
||||
ReminderMinutes = eventDto.ReminderMinutes,
|
||||
EventColor = eventDto.EventColor,
|
||||
StartDate = eventDto.StartDate,
|
||||
StartTime = eventDto.StartTime,
|
||||
EndDate = eventDto.EndDate,
|
||||
EndTime = eventDto.EndTime,
|
||||
AllDay = eventDto.AllDay,
|
||||
CreatedDate = DateTime.Now,
|
||||
IsDeleted = false
|
||||
};
|
||||
|
||||
_db.Events.Add(model);
|
||||
await _db.SaveChangesAsync();
|
||||
|
||||
return Ok(new DataResponse { Message = "Event Created Successfully", Status = "200" });
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return BadRequest(new Response { Status = "Error", Message = ex.Message });
|
||||
}
|
||||
}
|
||||
|
||||
// ===================================================================================
|
||||
// UPDATE EVENT
|
||||
// ===================================================================================
|
||||
[HttpPost]
|
||||
[Route("EditEvent")]
|
||||
public async Task<IActionResult> EditEvent(EditEvent_DTO eventDto)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (!eventDto.Id.HasValue)
|
||||
{
|
||||
return BadRequest(new Response { Status = "Error", Message = "Event ID is required for editing" });
|
||||
}
|
||||
|
||||
var model = await _db.Events.FindAsync(eventDto.Id.Value);
|
||||
if (model == null || model.IsDeleted == true)
|
||||
{
|
||||
return NotFound(new Response { Status = "Error", Message = "Event not found" });
|
||||
}
|
||||
|
||||
model.Title = eventDto.Title;
|
||||
model.Description = eventDto.Description;
|
||||
model.Location = eventDto.Location;
|
||||
model.RecordID = eventDto.RecordID;
|
||||
model.RecordName = eventDto.RecordName;
|
||||
model.RecordTargetType = eventDto.RecordTargetType;
|
||||
model.ReminderMinutes = eventDto.ReminderMinutes;
|
||||
model.EventColor = eventDto.EventColor;
|
||||
model.StartDate = eventDto.StartDate;
|
||||
model.StartTime = eventDto.StartTime;
|
||||
model.EndDate = eventDto.EndDate;
|
||||
model.EndTime = eventDto.EndTime;
|
||||
model.AllDay = eventDto.AllDay;
|
||||
model.LastModificationTime = DateTime.Now;
|
||||
|
||||
_db.Events.Update(model);
|
||||
await _db.SaveChangesAsync();
|
||||
|
||||
return Ok(new DataResponse { Message = "Event Updated Successfully", Status = "200" });
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return BadRequest(new Response { Status = "Error", Message = ex.Message });
|
||||
}
|
||||
}
|
||||
|
||||
// ===================================================================================
|
||||
// GET EVENT BY ID
|
||||
// ===================================================================================
|
||||
[HttpGet]
|
||||
[Route("GetEventById/{id}")]
|
||||
public async Task<IActionResult> GetEventById(int id)
|
||||
{
|
||||
try
|
||||
{
|
||||
var model = await _db.Events
|
||||
.Where(e => e.Id == id && e.IsDeleted != true)
|
||||
.FirstOrDefaultAsync();
|
||||
|
||||
if (model == null)
|
||||
{
|
||||
return NotFound(new Response { Status = "Error", Message = "Event not found" });
|
||||
}
|
||||
|
||||
var dto = new Event_DTO
|
||||
{
|
||||
Id = model.Id,
|
||||
Title = model.Title,
|
||||
Description = model.Description,
|
||||
Location = model.Location,
|
||||
RecordID = model.RecordID,
|
||||
RecordName = model.RecordName,
|
||||
RecordTargetType = model.RecordTargetType,
|
||||
ReminderMinutes = model.ReminderMinutes,
|
||||
EventColor = model.EventColor,
|
||||
StartDate = model.StartDate,
|
||||
StartTime = model.StartTime,
|
||||
EndDate = model.EndDate,
|
||||
EndTime = model.EndTime,
|
||||
AllDay = model.AllDay,
|
||||
CreatedDate = model.CreatedDate,
|
||||
createdby = model.createdby
|
||||
};
|
||||
|
||||
return Ok(new DataResponse { Message = "Success", Status = "200", Data = dto });
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return BadRequest(new Response { Status = "Error", Message = ex.Message });
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// ===================================================================================
|
||||
// DELETE EVENT
|
||||
// ===================================================================================
|
||||
[HttpDelete]
|
||||
[Route("DeleteEvent/{id}")]
|
||||
public async Task<IActionResult> DeleteEvent(int id)
|
||||
{
|
||||
try
|
||||
{
|
||||
var model = await _db.Events.FindAsync(id);
|
||||
if (model == null)
|
||||
{
|
||||
return NotFound(new Response { Status = "Error", Message = "Event not found" });
|
||||
}
|
||||
|
||||
// Soft delete
|
||||
model.IsDeleted = true;
|
||||
model.DeletionTime = DateTime.Now;
|
||||
|
||||
_db.Events.Update(model);
|
||||
await _db.SaveChangesAsync();
|
||||
|
||||
return Ok(new DataResponse { Message = "Event Deleted Successfully", Status = "200" });
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return BadRequest(new Response { Status = "Error", Message = ex.Message });
|
||||
}
|
||||
}
|
||||
|
||||
// ===================================================================================
|
||||
// GET EVENTS LIST (WITH PAGINATION AND FILTERS)
|
||||
// ===================================================================================
|
||||
[HttpPost]
|
||||
[Route("GetEventList")]
|
||||
public IActionResult GetEventList(Pagination_DTO dto)
|
||||
{
|
||||
try
|
||||
{
|
||||
var query = _db.Events.Where(e => e.IsDeleted != true);
|
||||
|
||||
// Apply search filter if provided
|
||||
if (!string.IsNullOrWhiteSpace(dto.Search))
|
||||
{
|
||||
query = query.Where(e =>
|
||||
e.Title.Contains(dto.Search) ||
|
||||
(e.Description != null && e.Description.Contains(dto.Search)) ||
|
||||
(e.Location != null && e.Location.Contains(dto.Search))
|
||||
);
|
||||
}
|
||||
|
||||
// IMPORTANT: Calculate totalCount AFTER applying filters
|
||||
var totalCount = query.Count();
|
||||
|
||||
// Apply pagination
|
||||
var events = query
|
||||
.OrderByDescending(e => e.StartDate)
|
||||
.ThenByDescending(e => e.CreatedDate)
|
||||
.Skip((dto.PageNumber - 1) * dto.PageSize)
|
||||
.Take(dto.PageSize)
|
||||
.Select(e => new Event_DTO
|
||||
{
|
||||
Id = e.Id,
|
||||
Title = e.Title,
|
||||
Description = e.Description,
|
||||
Location = e.Location,
|
||||
RecordID = e.RecordID,
|
||||
RecordName = e.RecordName,
|
||||
RecordTargetType = e.RecordTargetType,
|
||||
ReminderMinutes = e.ReminderMinutes,
|
||||
EventColor = e.EventColor,
|
||||
StartDate = e.StartDate,
|
||||
StartTime = e.StartTime,
|
||||
EndDate = e.EndDate,
|
||||
EndTime = e.EndTime,
|
||||
AllDay = e.AllDay,
|
||||
CreatedDate = e.CreatedDate,
|
||||
createdby = e.createdby
|
||||
})
|
||||
.ToList();
|
||||
|
||||
return Ok(new DataResponse
|
||||
{
|
||||
Message = "Success",
|
||||
Status = "200",
|
||||
Data = new { rows = events, totalCount }
|
||||
});
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return BadRequest(new Response { Status = "Error", Message = ex.Message });
|
||||
}
|
||||
}
|
||||
|
||||
// ===================================================================================
|
||||
// GET EVENTS FOR CALENDAR (Date Range Filter)
|
||||
// ===================================================================================
|
||||
[HttpGet]
|
||||
[Route("GetEventsForCalendar")]
|
||||
public IActionResult GetEventsForCalendar([FromQuery] DateTime? start, [FromQuery] DateTime? end)
|
||||
{
|
||||
try
|
||||
{
|
||||
var query = _db.Events.Where(e => e.IsDeleted != true);
|
||||
|
||||
// Filter by date range if provided
|
||||
if (start.HasValue && end.HasValue)
|
||||
{
|
||||
query = query.Where(e =>
|
||||
(e.StartDate >= start.Value && e.StartDate <= end.Value) ||
|
||||
(e.EndDate >= start.Value && e.EndDate <= end.Value) ||
|
||||
(e.StartDate <= start.Value && e.EndDate >= end.Value)
|
||||
);
|
||||
}
|
||||
|
||||
var events = query
|
||||
.Select(e => new Event_DTO
|
||||
{
|
||||
Id = e.Id,
|
||||
Title = e.Title,
|
||||
Description = e.Description,
|
||||
Location = e.Location,
|
||||
RecordID = e.RecordID,
|
||||
RecordName = e.RecordName,
|
||||
RecordTargetType = e.RecordTargetType,
|
||||
ReminderMinutes = e.ReminderMinutes,
|
||||
EventColor = e.EventColor,
|
||||
StartDate = e.StartDate,
|
||||
StartTime = e.StartTime,
|
||||
EndDate = e.EndDate,
|
||||
EndTime = e.EndTime,
|
||||
AllDay = e.AllDay,
|
||||
CreatedDate = e.CreatedDate,
|
||||
createdby = e.createdby
|
||||
})
|
||||
.ToList();
|
||||
|
||||
return Ok(new DataResponse { Message = "Success", Status = "200", Data = events });
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return BadRequest(new Response { Status = "Error", Message = ex.Message });
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
===================================================================================
|
||||
SECTION 4: DATABASE SETUP
|
||||
===================================================================================
|
||||
|
||||
STEP 1: Add Events DbSet to ApplicationDbContext.cs
|
||||
-------------------------------------------------------
|
||||
Add this line in the ApplicationDbContext class (around line 48):
|
||||
|
||||
public DbSet<Events> Events { get; set; }
|
||||
|
||||
|
||||
STEP 2: Create Database Migration
|
||||
-------------------------------------------------------
|
||||
Run these commands in the terminal from the Api.SeaHavenIndustries project directory:
|
||||
|
||||
dotnet ef migrations add AddEventsTable --project ../Data.SeaHavenIndustries
|
||||
dotnet ef database update
|
||||
|
||||
|
||||
STEP 3: SQL Script for Manual Table Creation (Alternative to migrations)
|
||||
-------------------------------------------------------
|
||||
*/
|
||||
|
||||
-- SQL SCRIPT TO CREATE EVENTS TABLE
|
||||
/*
|
||||
CREATE TABLE Events (
|
||||
Id INT PRIMARY KEY IDENTITY(1,1),
|
||||
Title NVARCHAR(100) NOT NULL,
|
||||
Description NVARCHAR(MAX) NULL,
|
||||
Location NVARCHAR(150) NULL,
|
||||
|
||||
RecordID INT NULL,
|
||||
RecordName NVARCHAR(255) NULL,
|
||||
RecordTargetType NVARCHAR(50) NULL,
|
||||
|
||||
ReminderMinutes INT NOT NULL DEFAULT 15,
|
||||
EventColor NVARCHAR(20) NOT NULL DEFAULT 'primary',
|
||||
|
||||
StartDate DATETIME NOT NULL,
|
||||
StartTime NVARCHAR(10) NULL,
|
||||
EndDate DATETIME NOT NULL,
|
||||
EndTime NVARCHAR(10) NULL,
|
||||
AllDay BIT NOT NULL DEFAULT 0,
|
||||
|
||||
-- Audit fields
|
||||
IsDeleted BIT NULL,
|
||||
createdby NVARCHAR(450) NULL,
|
||||
DeleterUserId NVARCHAR(450) NULL,
|
||||
DeletionTime DATETIME NULL,
|
||||
CreatedDate DATETIME NULL,
|
||||
LastModificationTime DATETIME NULL,
|
||||
LastModifierUserId INT NULL
|
||||
);
|
||||
|
||||
-- Insert sample data
|
||||
INSERT INTO Events (Title, Description, Location, StartDate, StartTime, EndDate, EndTime, AllDay, EventColor, ReminderMinutes, CreatedDate, IsDeleted)
|
||||
VALUES
|
||||
('Team Meeting', 'Weekly team sync', 'Conference Room A', '2026-04-01', '10:00 AM', '2026-04-01', '11:00 AM', 0, 'primary', 15, GETDATE(), 0),
|
||||
('Client Presentation', 'Q1 Results Review with ABC Corp', 'Virtual - Zoom', '2026-04-03', '02:00 PM', '2026-04-03', '03:30 PM', 0, 'success', 30, GETDATE(), 0),
|
||||
('Project Deadline', 'Final delivery for Project Phoenix', NULL, '2026-04-05', NULL, '2026-04-05', NULL, 1, 'danger', 1440, GETDATE(), 0),
|
||||
('Training Session', 'New system training for all employees', 'Main Office', '2026-04-08', '09:00 AM', '2026-04-08', '12:00 PM', 0, 'info', 60, GETDATE(), 0),
|
||||
('Equipment Maintenance', 'Scheduled maintenance for all assets', 'Warehouse', '2026-04-10', '08:00 AM', '2026-04-10', '05:00 PM', 0, 'warning', 120, GETDATE(), 0);
|
||||
*/
|
||||
|
||||
/*
|
||||
===================================================================================
|
||||
SECTION 5: IMPLEMENTATION CHECKLIST
|
||||
===================================================================================
|
||||
|
||||
BACKEND SETUP:
|
||||
--------------
|
||||
1. ✓ Create Events.cs in Data.SeaHavenIndustries/Models/
|
||||
2. ✓ Create Event_DTO.cs in Api.SeaHavenIndustries/DTOs/
|
||||
3. ✓ Create EditEvent_DTO.cs in Api.SeaHavenIndustries/DTOs/
|
||||
4. ✓ Create CalendarController.cs in Api.SeaHavenIndustries/Controllers/
|
||||
5. ☐ Add DbSet<Events> to ApplicationDbContext.cs
|
||||
6. ☐ Run migrations (or execute SQL script)
|
||||
7. ☐ Test all endpoints using Postman/Swagger
|
||||
|
||||
FRONTEND SETUP (Next Step):
|
||||
---------------------------
|
||||
1. ☐ Create src/pages/calendar/CalendarPage.js (List view with FullCalendar)
|
||||
2. ☐ Create src/pages/calendar/EventFormPage.js (Create/Edit form)
|
||||
3. ☐ Create src/pages/calendar/api.js (API calls)
|
||||
4. ☐ Add routes to App.js
|
||||
5. ☐ Install FullCalendar: npm install @fullcalendar/react @fullcalendar/daygrid @fullcalendar/timegrid @fullcalendar/interaction
|
||||
|
||||
API ENDPOINTS SUMMARY:
|
||||
---------------------
|
||||
POST /api/Calendar/AddEvent - Create new event
|
||||
POST /api/Calendar/EditEvent - Update existing event
|
||||
GET /api/Calendar/GetEventById/{id} - Get single event by ID
|
||||
DELETE /api/Calendar/DeleteEvent/{id} - Delete event (soft delete)
|
||||
POST /api/Calendar/GetEventList - Get paginated list with search
|
||||
GET /api/Calendar/GetEventsForCalendar - Get events for calendar view (with date range)
|
||||
|
||||
KEY FEATURES IMPLEMENTED:
|
||||
-------------------------
|
||||
✓ CRUD operations (Create, Read, Update, Delete)
|
||||
✓ Pagination with search filter
|
||||
✓ Date range filtering for calendar view
|
||||
✓ Soft delete (IsDeleted flag)
|
||||
✓ Related record linking (RecordID, RecordName, RecordTargetType)
|
||||
✓ Color coding for calendar display
|
||||
✓ All-day event support
|
||||
✓ Reminder settings
|
||||
✓ Proper error handling
|
||||
✓ Consistent with existing controller patterns (AssetController)
|
||||
✓ Fixed pagination bug (totalCount calculated AFTER filters)
|
||||
|
||||
NOTES:
|
||||
------
|
||||
- EventColor values: 'primary', 'success', 'info', 'warning', 'danger'
|
||||
- ReminderMinutes: 0=Never, 5, 10, 15, 30, 60, 120, 240, 360, 720, 1440 (1 day)
|
||||
- StartTime/EndTime format: "08:00 AM", "02:30 PM", etc.
|
||||
- AllDay: When true, time fields can be null/ignored
|
||||
- RecordTargetType: Can be "Account", "Contact", "WorkOrder", "FollowUp", etc.
|
||||
|
||||
===================================================================================
|
||||
END OF FILE
|
||||
===================================================================================
|
||||
*/
|
||||
92
Data.SeaHavenIndustries/Enums/WorkOrderEnums.cs
Normal file
92
Data.SeaHavenIndustries/Enums/WorkOrderEnums.cs
Normal file
|
|
@ -0,0 +1,92 @@
|
|||
namespace Data.SeaHavenIndustries.Enums
|
||||
{
|
||||
public enum WorkOrderStatus
|
||||
{
|
||||
Open = 1,
|
||||
InProgress = 2,
|
||||
Completed = 3,
|
||||
Cancelled = 4,
|
||||
OnHold = 5
|
||||
}
|
||||
|
||||
public enum WorkOrderPriority
|
||||
{
|
||||
Low = 1,
|
||||
Medium = 2,
|
||||
High = 3,
|
||||
Critical = 4
|
||||
}
|
||||
|
||||
public enum AuditAction
|
||||
{
|
||||
Create = 1,
|
||||
Edit = 2,
|
||||
Delete = 3,
|
||||
StatusChange = 4,
|
||||
AssignmentChange = 5,
|
||||
Dispatch = 6,
|
||||
DispatchUpdate = 7,
|
||||
DispatchVerify = 8
|
||||
}
|
||||
|
||||
public enum ContactType
|
||||
{
|
||||
Primary = 1,
|
||||
Secondary = 2,
|
||||
Billing = 3,
|
||||
Technical = 4
|
||||
}
|
||||
|
||||
public enum AssetStatus
|
||||
{
|
||||
Active = 1,
|
||||
Inactive = 2,
|
||||
UnderMaintenance = 3,
|
||||
Retired = 4
|
||||
}
|
||||
|
||||
public enum DispatchStatus
|
||||
{
|
||||
Pending = 1,
|
||||
Assigned = 2,
|
||||
InProgress = 3,
|
||||
Completed = 4,
|
||||
Cancelled = 5
|
||||
}
|
||||
|
||||
public enum FollowUpStatus
|
||||
{
|
||||
Open = 1,
|
||||
InProgress = 2,
|
||||
Resolved = 3,
|
||||
Closed = 4
|
||||
}
|
||||
|
||||
public enum PMScheduleFrequency
|
||||
{
|
||||
Daily = 1,
|
||||
Weekly = 2,
|
||||
Monthly = 3,
|
||||
Quarterly = 4,
|
||||
SemiAnnual = 5,
|
||||
Annual = 6
|
||||
}
|
||||
|
||||
public enum CommentType
|
||||
{
|
||||
General = 1,
|
||||
Internal = 2,
|
||||
Customer = 3,
|
||||
Vendor = 4,
|
||||
System = 5
|
||||
}
|
||||
|
||||
public enum RecordType
|
||||
{
|
||||
WorkOrder = 1,
|
||||
Dispatch = 2,
|
||||
Quote = 3,
|
||||
Contact = 4,
|
||||
Asset = 5
|
||||
}
|
||||
}
|
||||
|
|
@ -1,109 +0,0 @@
|
|||
-- SQL Script to add missing columns to Locations table
|
||||
-- Run this in SQL Server Management Studio
|
||||
|
||||
USE [db_a7141e_seahavenindustry]
|
||||
GO
|
||||
|
||||
-- Check and add AccountId column
|
||||
IF NOT EXISTS (SELECT * FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME = 'Locations' AND COLUMN_NAME = 'AccountId')
|
||||
BEGIN
|
||||
ALTER TABLE [dbo].[Locations] ADD [AccountId] int NULL;
|
||||
PRINT 'Added AccountId column';
|
||||
END
|
||||
ELSE
|
||||
PRINT 'AccountId column already exists';
|
||||
|
||||
-- Check and add Address column
|
||||
IF NOT EXISTS (SELECT * FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME = 'Locations' AND COLUMN_NAME = 'Address')
|
||||
BEGIN
|
||||
ALTER TABLE [dbo].[Locations] ADD [Address] nvarchar(max) NULL;
|
||||
PRINT 'Added Address column';
|
||||
END
|
||||
ELSE
|
||||
PRINT 'Address column already exists';
|
||||
|
||||
-- Check and add City column
|
||||
IF NOT EXISTS (SELECT * FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME = 'Locations' AND COLUMN_NAME = 'City')
|
||||
BEGIN
|
||||
ALTER TABLE [dbo].[Locations] ADD [City] nvarchar(max) NULL;
|
||||
PRINT 'Added City column';
|
||||
END
|
||||
ELSE
|
||||
PRINT 'City column already exists';
|
||||
|
||||
-- Check and add Contact column
|
||||
IF NOT EXISTS (SELECT * FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME = 'Locations' AND COLUMN_NAME = 'Contact')
|
||||
BEGIN
|
||||
ALTER TABLE [dbo].[Locations] ADD [Contact] nvarchar(max) NULL;
|
||||
PRINT 'Added Contact column';
|
||||
END
|
||||
ELSE
|
||||
PRINT 'Contact column already exists';
|
||||
|
||||
-- Check and add ContactEmail column
|
||||
IF NOT EXISTS (SELECT * FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME = 'Locations' AND COLUMN_NAME = 'ContactEmail')
|
||||
BEGIN
|
||||
ALTER TABLE [dbo].[Locations] ADD [ContactEmail] nvarchar(max) NULL;
|
||||
PRINT 'Added ContactEmail column';
|
||||
END
|
||||
ELSE
|
||||
PRINT 'ContactEmail column already exists';
|
||||
|
||||
-- Check and add Phone column
|
||||
IF NOT EXISTS (SELECT * FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME = 'Locations' AND COLUMN_NAME = 'Phone')
|
||||
BEGIN
|
||||
ALTER TABLE [dbo].[Locations] ADD [Phone] nvarchar(max) NULL;
|
||||
PRINT 'Added Phone column';
|
||||
END
|
||||
ELSE
|
||||
PRINT 'Phone column already exists';
|
||||
|
||||
-- Check and add State column
|
||||
IF NOT EXISTS (SELECT * FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME = 'Locations' AND COLUMN_NAME = 'State')
|
||||
BEGIN
|
||||
ALTER TABLE [dbo].[Locations] ADD [State] nvarchar(max) NULL;
|
||||
PRINT 'Added State column';
|
||||
END
|
||||
ELSE
|
||||
PRINT 'State column already exists';
|
||||
|
||||
-- Check and add Status column
|
||||
IF NOT EXISTS (SELECT * FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME = 'Locations' AND COLUMN_NAME = 'Status')
|
||||
BEGIN
|
||||
ALTER TABLE [dbo].[Locations] ADD [Status] nvarchar(max) NULL;
|
||||
PRINT 'Added Status column';
|
||||
END
|
||||
ELSE
|
||||
PRINT 'Status column already exists';
|
||||
|
||||
-- Check and add ZipCode column
|
||||
IF NOT EXISTS (SELECT * FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME = 'Locations' AND COLUMN_NAME = 'ZipCode')
|
||||
BEGIN
|
||||
ALTER TABLE [dbo].[Locations] ADD [ZipCode] nvarchar(max) NULL;
|
||||
PRINT 'Added ZipCode column';
|
||||
END
|
||||
ELSE
|
||||
PRINT 'ZipCode column already exists';
|
||||
|
||||
-- Check and add foreign key index
|
||||
IF NOT EXISTS (SELECT * FROM sys.indexes WHERE name = 'IX_Locations_AccountId' AND object_id = OBJECT_ID('dbo.Locations'))
|
||||
BEGIN
|
||||
CREATE NONCLUSTERED INDEX [IX_Locations_AccountId] ON [dbo].[Locations] ([AccountId] ASC);
|
||||
PRINT 'Added IX_Locations_AccountId index';
|
||||
END
|
||||
ELSE
|
||||
PRINT 'IX_Locations_AccountId index already exists';
|
||||
|
||||
-- Check and add foreign key constraint if Accounts table exists
|
||||
IF EXISTS (SELECT * FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME = 'Accounts')
|
||||
AND NOT EXISTS (SELECT * FROM sys.foreign_keys WHERE name = 'FK_Locations_Accounts_AccountId')
|
||||
BEGIN
|
||||
ALTER TABLE [dbo].[Locations] WITH CHECK ADD CONSTRAINT [FK_Locations_Accounts_AccountId]
|
||||
FOREIGN KEY([AccountId]) REFERENCES [dbo].[Accounts] ([Id]);
|
||||
PRINT 'Added FK_Locations_Accounts_AccountId foreign key';
|
||||
END
|
||||
ELSE
|
||||
PRINT 'FK_Locations_Accounts_AccountId foreign key already exists or Accounts table does not exist';
|
||||
|
||||
PRINT 'Schema fix completed!';
|
||||
GO
|
||||
|
|
@ -0,0 +1,35 @@
|
|||
using Microsoft.Extensions.DependencyInjection;
|
||||
using System.Reflection;
|
||||
|
||||
namespace SeaHaven.DataServices.DependencyInjection
|
||||
{
|
||||
public static class DataServicesModule
|
||||
{
|
||||
private static readonly List<Type> ExcludedTypes = new();
|
||||
|
||||
public static IServiceCollection AddDataServices(this IServiceCollection services)
|
||||
{
|
||||
var assembly = Assembly.GetExecutingAssembly();
|
||||
|
||||
var allClasses = assembly.GetTypes()
|
||||
.Where(t => t.IsClass &&
|
||||
!t.IsAbstract &&
|
||||
!t.IsGenericType &&
|
||||
!ExcludedTypes.Contains(t))
|
||||
.ToList();
|
||||
|
||||
foreach (var implementationType in allClasses)
|
||||
{
|
||||
var defaultInterface = implementationType.GetInterfaces()
|
||||
.FirstOrDefault(i => i.Name == $"I{implementationType.Name}");
|
||||
|
||||
if (defaultInterface != null)
|
||||
{
|
||||
services.AddScoped(defaultInterface, implementationType);
|
||||
}
|
||||
}
|
||||
|
||||
return services;
|
||||
}
|
||||
}
|
||||
}
|
||||
75
SeaHaven.DataServices/Implementation/CommentDataService.cs
Normal file
75
SeaHaven.DataServices/Implementation/CommentDataService.cs
Normal file
|
|
@ -0,0 +1,75 @@
|
|||
using Data.SeaHavenIndustries;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using SeaHaven.DataServices.Interfaces;
|
||||
|
||||
namespace SeaHaven.DataServices.Implementation
|
||||
{
|
||||
public class CommentDataService : ICommentDataService
|
||||
{
|
||||
private readonly ApplicationDbContext _context;
|
||||
|
||||
public CommentDataService(ApplicationDbContext context)
|
||||
{
|
||||
_context = context;
|
||||
}
|
||||
|
||||
public async Task<Comments?> GetByIdAsync(int id)
|
||||
{
|
||||
return await _context.Comments.FindAsync(id);
|
||||
}
|
||||
|
||||
public async Task<IEnumerable<Comments>> GetAllAsync()
|
||||
{
|
||||
return await _context.Comments
|
||||
.Include(c => c.ApplicationUser)
|
||||
.ToListAsync();
|
||||
}
|
||||
|
||||
public async Task<IEnumerable<Comments>> GetByWorkOrderIdAsync(int workOrderId)
|
||||
{
|
||||
return await _context.Comments
|
||||
.Where(c => c.WorkerOrderId == workOrderId)
|
||||
.Include(c => c.ApplicationUser)
|
||||
.OrderBy(c => c.CreatedDate)
|
||||
.ToListAsync();
|
||||
}
|
||||
|
||||
public async Task<IEnumerable<Comments>> GetByDispatchIdAsync(int dispatchId)
|
||||
{
|
||||
return await _context.Comments
|
||||
.Where(c => c.DispatchId == dispatchId)
|
||||
.Include(c => c.ApplicationUser)
|
||||
.OrderBy(c => c.CreatedDate)
|
||||
.ToListAsync();
|
||||
}
|
||||
|
||||
public async Task<Comments> AddAsync(Comments comment)
|
||||
{
|
||||
comment.CreatedDate = DateTime.UtcNow;
|
||||
await _context.Comments.AddAsync(comment);
|
||||
await _context.SaveChangesAsync();
|
||||
return comment;
|
||||
}
|
||||
|
||||
public async Task UpdateAsync(Comments comment)
|
||||
{
|
||||
_context.Comments.Update(comment);
|
||||
await _context.SaveChangesAsync();
|
||||
}
|
||||
|
||||
public async Task DeleteAsync(int id)
|
||||
{
|
||||
var entity = await GetByIdAsync(id);
|
||||
if (entity != null)
|
||||
{
|
||||
_context.Comments.Remove(entity);
|
||||
await _context.SaveChangesAsync();
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<bool> ExistsAsync(int id)
|
||||
{
|
||||
return await _context.Comments.AnyAsync(c => c.Id == id);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -111,5 +111,110 @@ namespace SeaHaven.DataServices.Implementation
|
|||
{
|
||||
return await _context.Dispatches.CountAsync();
|
||||
}
|
||||
|
||||
public async Task<IEnumerable<Dispatch>> GetByWorkOrderIdAsync(int workOrderId)
|
||||
{
|
||||
var dispatchIds = await _context.DispatchWorkOrders
|
||||
.Where(dwo => dwo.WorkOrderId == workOrderId)
|
||||
.Select(dwo => dwo.DispatchId)
|
||||
.Distinct()
|
||||
.ToListAsync();
|
||||
|
||||
var directIds = await _context.Dispatches
|
||||
.Where(d => d.WorkOrderId == workOrderId)
|
||||
.Select(d => d.Id)
|
||||
.ToListAsync();
|
||||
|
||||
var allIds = dispatchIds.Union(directIds).Distinct().ToList();
|
||||
|
||||
return await _context.Dispatches
|
||||
.Where(d => allIds.Contains(d.Id))
|
||||
.Include(d => d.Vendor)
|
||||
.ToListAsync();
|
||||
}
|
||||
|
||||
public async Task<IEnumerable<Dispatch>> GetByWorkOrderIdsAsync(IEnumerable<int> workOrderIds)
|
||||
{
|
||||
return await _context.Dispatches
|
||||
.Where(d => workOrderIds.Contains(d.WorkOrderId ?? 0))
|
||||
.Include(d => d.Vendor)
|
||||
.ToListAsync();
|
||||
}
|
||||
|
||||
public async Task<string> GenerateNextPONumberAsync()
|
||||
{
|
||||
var lastPO = await _context.Dispatches
|
||||
.Where(d => d.PONumber != null)
|
||||
.OrderByDescending(d => d.PONumber)
|
||||
.Select(d => d.PONumber)
|
||||
.FirstOrDefaultAsync();
|
||||
|
||||
int nextPO = 1000;
|
||||
if (!string.IsNullOrEmpty(lastPO) && int.TryParse(lastPO, out int lastNum))
|
||||
{
|
||||
nextPO = lastNum + 1;
|
||||
}
|
||||
|
||||
return nextPO.ToString();
|
||||
}
|
||||
|
||||
public async Task<IEnumerable<DispatchChecklistItem>> GetChecklistItemsAsync(int dispatchId)
|
||||
{
|
||||
return await _context.DispatchChecklistItems
|
||||
.Where(ci => ci.DispatchId == dispatchId)
|
||||
.OrderBy(ci => ci.SortOrder)
|
||||
.ToListAsync();
|
||||
}
|
||||
|
||||
public async Task<DispatchChecklistItem?> GetChecklistItemByIdAsync(int id)
|
||||
{
|
||||
return await _context.DispatchChecklistItems.FindAsync(id);
|
||||
}
|
||||
|
||||
public async Task AddChecklistItemAsync(DispatchChecklistItem item)
|
||||
{
|
||||
await _context.DispatchChecklistItems.AddAsync(item);
|
||||
await _context.SaveChangesAsync();
|
||||
}
|
||||
|
||||
public async Task UpdateChecklistItemAsync(DispatchChecklistItem item)
|
||||
{
|
||||
_context.DispatchChecklistItems.Update(item);
|
||||
await _context.SaveChangesAsync();
|
||||
}
|
||||
|
||||
public async Task<IEnumerable<DispatchSignoff>> GetSignoffsAsync(int dispatchId)
|
||||
{
|
||||
return await _context.DispatchSignoffs
|
||||
.Where(s => s.DispatchId == dispatchId)
|
||||
.ToListAsync();
|
||||
}
|
||||
|
||||
public async Task AddSignoffAsync(DispatchSignoff signoff)
|
||||
{
|
||||
signoff.SignedAt = DateTime.UtcNow;
|
||||
await _context.DispatchSignoffs.AddAsync(signoff);
|
||||
await _context.SaveChangesAsync();
|
||||
}
|
||||
|
||||
public async Task<bool> HasSignoffTypeAsync(int dispatchId, string signoffType)
|
||||
{
|
||||
return await _context.DispatchSignoffs
|
||||
.AnyAsync(s => s.DispatchId == dispatchId && s.SignoffType == signoffType);
|
||||
}
|
||||
|
||||
public async Task<IEnumerable<DispatchWorkOrder>> GetDispatchWorkOrdersAsync(int dispatchId)
|
||||
{
|
||||
return await _context.DispatchWorkOrders
|
||||
.Where(dwo => dwo.DispatchId == dispatchId)
|
||||
.Include(dwo => dwo.WorkOrder)
|
||||
.ToListAsync();
|
||||
}
|
||||
|
||||
public async Task AddDispatchWorkOrderAsync(DispatchWorkOrder dispatchWorkOrder)
|
||||
{
|
||||
await _context.DispatchWorkOrders.AddAsync(dispatchWorkOrder);
|
||||
await _context.SaveChangesAsync();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
75
SeaHaven.DataServices/Implementation/QuotesDataService.cs
Normal file
75
SeaHaven.DataServices/Implementation/QuotesDataService.cs
Normal file
|
|
@ -0,0 +1,75 @@
|
|||
using Data.SeaHavenIndustries;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using SeaHaven.DataServices.Interfaces;
|
||||
|
||||
namespace SeaHaven.DataServices.Implementation
|
||||
{
|
||||
public class QuotesDataService : IQuotesDataService
|
||||
{
|
||||
private readonly ApplicationDbContext _context;
|
||||
|
||||
public QuotesDataService(ApplicationDbContext context)
|
||||
{
|
||||
_context = context;
|
||||
}
|
||||
|
||||
public async Task<Quotes?> GetByIdAsync(int id)
|
||||
{
|
||||
return await _context.Quotes.FindAsync(id);
|
||||
}
|
||||
|
||||
public async Task<IEnumerable<Quotes>> GetAllAsync()
|
||||
{
|
||||
return await _context.Quotes.ToListAsync();
|
||||
}
|
||||
|
||||
public async Task<IEnumerable<Quotes>> GetByWorkOrderIdAsync(int workOrderId)
|
||||
{
|
||||
return await _context.Quotes
|
||||
.Where(q => q.WorkorderId == workOrderId)
|
||||
.ToListAsync();
|
||||
}
|
||||
|
||||
public async Task<Quotes> AddAsync(Quotes quote)
|
||||
{
|
||||
await _context.Quotes.AddAsync(quote);
|
||||
await _context.SaveChangesAsync();
|
||||
return quote;
|
||||
}
|
||||
|
||||
public async Task UpdateAsync(Quotes quote)
|
||||
{
|
||||
_context.Quotes.Update(quote);
|
||||
await _context.SaveChangesAsync();
|
||||
}
|
||||
|
||||
public async Task DeleteAsync(int id)
|
||||
{
|
||||
var entity = await GetByIdAsync(id);
|
||||
if (entity != null)
|
||||
{
|
||||
_context.Quotes.Remove(entity);
|
||||
await _context.SaveChangesAsync();
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<bool> ExistsAsync(int id)
|
||||
{
|
||||
return await _context.Quotes.AnyAsync(q => q.Id == id);
|
||||
}
|
||||
|
||||
public async Task UnlinkFromWorkOrderAsync(int workOrderId)
|
||||
{
|
||||
var quotes = await _context.Quotes
|
||||
.Where(q => q.WorkorderId == workOrderId)
|
||||
.ToListAsync();
|
||||
|
||||
foreach (var quote in quotes)
|
||||
{
|
||||
quote.WorkorderId = null;
|
||||
}
|
||||
|
||||
await _context.SaveChangesAsync();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,61 @@
|
|||
using Data.SeaHavenIndustries;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using SeaHaven.DataServices.Interfaces;
|
||||
|
||||
namespace SeaHaven.DataServices.Implementation
|
||||
{
|
||||
public class TaskListTemplateDataService : ITaskListTemplateDataService
|
||||
{
|
||||
private readonly ApplicationDbContext _context;
|
||||
|
||||
public TaskListTemplateDataService(ApplicationDbContext context)
|
||||
{
|
||||
_context = context;
|
||||
}
|
||||
|
||||
public async Task<TaskListTemplate?> GetByIdAsync(int id)
|
||||
{
|
||||
return await _context.TaskListTemplates.FindAsync(id);
|
||||
}
|
||||
|
||||
public async Task<TaskListTemplate?> GetByIdWithItemsAsync(int id)
|
||||
{
|
||||
return await _context.TaskListTemplates
|
||||
.Include(t => t.Items)
|
||||
.FirstOrDefaultAsync(t => t.Id == id);
|
||||
}
|
||||
|
||||
public async Task<IEnumerable<TaskListTemplate>> GetAllAsync()
|
||||
{
|
||||
return await _context.TaskListTemplates.ToListAsync();
|
||||
}
|
||||
|
||||
public async Task<TaskListTemplate> AddAsync(TaskListTemplate template)
|
||||
{
|
||||
await _context.TaskListTemplates.AddAsync(template);
|
||||
await _context.SaveChangesAsync();
|
||||
return template;
|
||||
}
|
||||
|
||||
public async Task UpdateAsync(TaskListTemplate template)
|
||||
{
|
||||
_context.TaskListTemplates.Update(template);
|
||||
await _context.SaveChangesAsync();
|
||||
}
|
||||
|
||||
public async Task DeleteAsync(int id)
|
||||
{
|
||||
var entity = await GetByIdAsync(id);
|
||||
if (entity != null)
|
||||
{
|
||||
_context.TaskListTemplates.Remove(entity);
|
||||
await _context.SaveChangesAsync();
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<bool> ExistsAsync(int id)
|
||||
{
|
||||
return await _context.TaskListTemplates.AnyAsync(t => t.Id == id);
|
||||
}
|
||||
}
|
||||
}
|
||||
37
SeaHaven.DataServices/Implementation/UserDataService.cs
Normal file
37
SeaHaven.DataServices/Implementation/UserDataService.cs
Normal file
|
|
@ -0,0 +1,37 @@
|
|||
using Data.SeaHavenIndustries;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using SeaHaven.DataServices.Interfaces;
|
||||
|
||||
namespace SeaHaven.DataServices.Implementation
|
||||
{
|
||||
public class UserDataService : IUserDataService
|
||||
{
|
||||
private readonly ApplicationDbContext _context;
|
||||
|
||||
public UserDataService(ApplicationDbContext context)
|
||||
{
|
||||
_context = context;
|
||||
}
|
||||
|
||||
public async Task<ApplicationUser?> GetByIdAsync(string id)
|
||||
{
|
||||
return await _context.Users.FindAsync(id);
|
||||
}
|
||||
|
||||
public async Task<IEnumerable<ApplicationUser>> GetAllAsync()
|
||||
{
|
||||
return await _context.Users.ToListAsync();
|
||||
}
|
||||
|
||||
public async Task<ApplicationUser?> GetByEmailAsync(string email)
|
||||
{
|
||||
return await _context.Users
|
||||
.FirstOrDefaultAsync(u => u.Email == email);
|
||||
}
|
||||
|
||||
public async Task<bool> ExistsAsync(string id)
|
||||
{
|
||||
return await _context.Users.AnyAsync(u => u.Id == id);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -239,6 +239,89 @@ namespace SeaHaven.DataServices.Implementation
|
|||
.ToListAsync();
|
||||
}
|
||||
|
||||
public async Task<IEnumerable<WorkOrderAuditLog>> GetWorkOrderAuditLogsAsync(int workOrderId)
|
||||
{
|
||||
return await _context.WorkOrderAuditLogs
|
||||
.Where(a => a.WorkOrderId == workOrderId)
|
||||
.OrderByDescending(a => a.CreatedAt)
|
||||
.ToListAsync();
|
||||
}
|
||||
|
||||
public async Task AddWorkOrderContactsAsync(int workOrderId, IEnumerable<int> contactIds)
|
||||
{
|
||||
var contacts = contactIds.Select(cid => new WorkOrderContacts
|
||||
{
|
||||
WorkorderId = workOrderId,
|
||||
ContactId = cid
|
||||
});
|
||||
|
||||
await _context.WorkOrderContacts.AddRangeAsync(contacts);
|
||||
await _context.SaveChangesAsync();
|
||||
}
|
||||
|
||||
public async Task AddWorkOrderCategoriesAsync(int workOrderId, IEnumerable<int> categoryIds)
|
||||
{
|
||||
var categories = categoryIds.Select(catId => new WorkOrderCategories
|
||||
{
|
||||
WorkorderId = workOrderId,
|
||||
CategoryId = catId
|
||||
});
|
||||
|
||||
await _context.workOrderCategories.AddRangeAsync(categories);
|
||||
await _context.SaveChangesAsync();
|
||||
}
|
||||
|
||||
public async Task AddWorkOrderAttachmentAsync(WorkOrderAttachments attachment)
|
||||
{
|
||||
await _context.workOrderAttachments.AddAsync(attachment);
|
||||
await _context.SaveChangesAsync();
|
||||
}
|
||||
|
||||
public async Task AddAuditLogAsync(WorkOrderAuditLog auditLog)
|
||||
{
|
||||
await _context.WorkOrderAuditLogs.AddAsync(auditLog);
|
||||
await _context.SaveChangesAsync();
|
||||
}
|
||||
|
||||
public async Task DeleteWorkOrderContactsAsync(int workOrderId)
|
||||
{
|
||||
var contacts = await _context.WorkOrderContacts
|
||||
.Where(wc => wc.WorkorderId == workOrderId)
|
||||
.ToListAsync();
|
||||
|
||||
if (contacts.Any())
|
||||
{
|
||||
_context.WorkOrderContacts.RemoveRange(contacts);
|
||||
await _context.SaveChangesAsync();
|
||||
}
|
||||
}
|
||||
|
||||
public async Task DeleteWorkOrderCategoriesAsync(int workOrderId)
|
||||
{
|
||||
var categories = await _context.workOrderCategories
|
||||
.Where(wc => wc.WorkorderId == workOrderId)
|
||||
.ToListAsync();
|
||||
|
||||
if (categories.Any())
|
||||
{
|
||||
_context.workOrderCategories.RemoveRange(categories);
|
||||
await _context.SaveChangesAsync();
|
||||
}
|
||||
}
|
||||
|
||||
public async Task DeleteWorkOrderAttachmentsAsync(int workOrderId)
|
||||
{
|
||||
var attachments = await _context.workOrderAttachments
|
||||
.Where(wa => wa.WorkorderId == workOrderId)
|
||||
.ToListAsync();
|
||||
|
||||
if (attachments.Any())
|
||||
{
|
||||
_context.workOrderAttachments.RemoveRange(attachments);
|
||||
await _context.SaveChangesAsync();
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<bool> ExistsAsync(int id)
|
||||
{
|
||||
return await _context.workOrders.AnyAsync(w => w.Id == id);
|
||||
|
|
@ -270,5 +353,15 @@ namespace SeaHaven.DataServices.Implementation
|
|||
{
|
||||
return await _context.workOrders.CountAsync(w => w.LocationId == locationId);
|
||||
}
|
||||
|
||||
public async Task<IEnumerable<WorkOrder>> GetAllWithDetailsAsync()
|
||||
{
|
||||
return await _context.workOrders
|
||||
.Where(w => w.istemplate != true)
|
||||
.Include(w => w.Locations)
|
||||
.Include(w => w.AssignToUser)
|
||||
.AsSplitQuery()
|
||||
.ToListAsync();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
16
SeaHaven.DataServices/Interfaces/ICommentDataService.cs
Normal file
16
SeaHaven.DataServices/Interfaces/ICommentDataService.cs
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
using Data.SeaHavenIndustries;
|
||||
|
||||
namespace SeaHaven.DataServices.Interfaces
|
||||
{
|
||||
public interface ICommentDataService
|
||||
{
|
||||
Task<Comments?> GetByIdAsync(int id);
|
||||
Task<IEnumerable<Comments>> GetAllAsync();
|
||||
Task<IEnumerable<Comments>> GetByWorkOrderIdAsync(int workOrderId);
|
||||
Task<IEnumerable<Comments>> GetByDispatchIdAsync(int dispatchId);
|
||||
Task<Comments> AddAsync(Comments comment);
|
||||
Task UpdateAsync(Comments comment);
|
||||
Task DeleteAsync(int id);
|
||||
Task<bool> ExistsAsync(int id);
|
||||
}
|
||||
}
|
||||
|
|
@ -8,6 +8,8 @@ namespace SeaHaven.DataServices.Interfaces
|
|||
Task<Dispatch?> GetByIdWithDetailsAsync(int id);
|
||||
Task<IEnumerable<Dispatch>> GetAllAsync();
|
||||
Task<IEnumerable<Dispatch>> GetByVendorIdAsync(int vendorId);
|
||||
Task<IEnumerable<Dispatch>> GetByWorkOrderIdAsync(int workOrderId);
|
||||
Task<IEnumerable<Dispatch>> GetByWorkOrderIdsAsync(IEnumerable<int> workOrderIds);
|
||||
Task<IEnumerable<Dispatch>> GetByStatusAsync(string status);
|
||||
Task<(IEnumerable<Dispatch> Items, int TotalCount)> GetPagedAsync(int page, int pageSize, string? search = null, string? status = null);
|
||||
Task<Dispatch> AddAsync(Dispatch dispatch);
|
||||
|
|
@ -15,6 +17,16 @@ namespace SeaHaven.DataServices.Interfaces
|
|||
Task DeleteAsync(int id);
|
||||
Task<bool> ExistsAsync(int id);
|
||||
Task<string> GenerateNextDispatchNumberAsync();
|
||||
Task<string> GenerateNextPONumberAsync();
|
||||
Task<int> CountAsync();
|
||||
Task<IEnumerable<DispatchChecklistItem>> GetChecklistItemsAsync(int dispatchId);
|
||||
Task<DispatchChecklistItem?> GetChecklistItemByIdAsync(int id);
|
||||
Task AddChecklistItemAsync(DispatchChecklistItem item);
|
||||
Task UpdateChecklistItemAsync(DispatchChecklistItem item);
|
||||
Task<IEnumerable<DispatchSignoff>> GetSignoffsAsync(int dispatchId);
|
||||
Task AddSignoffAsync(DispatchSignoff signoff);
|
||||
Task<bool> HasSignoffTypeAsync(int dispatchId, string signoffType);
|
||||
Task<IEnumerable<DispatchWorkOrder>> GetDispatchWorkOrdersAsync(int dispatchId);
|
||||
Task AddDispatchWorkOrderAsync(DispatchWorkOrder dispatchWorkOrder);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
16
SeaHaven.DataServices/Interfaces/IQuotesDataService.cs
Normal file
16
SeaHaven.DataServices/Interfaces/IQuotesDataService.cs
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
using Data.SeaHavenIndustries;
|
||||
|
||||
namespace SeaHaven.DataServices.Interfaces
|
||||
{
|
||||
public interface IQuotesDataService
|
||||
{
|
||||
Task<Quotes?> GetByIdAsync(int id);
|
||||
Task<IEnumerable<Quotes>> GetAllAsync();
|
||||
Task<IEnumerable<Quotes>> GetByWorkOrderIdAsync(int workOrderId);
|
||||
Task<Quotes> AddAsync(Quotes quote);
|
||||
Task UpdateAsync(Quotes quote);
|
||||
Task DeleteAsync(int id);
|
||||
Task<bool> ExistsAsync(int id);
|
||||
Task UnlinkFromWorkOrderAsync(int workOrderId);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,15 @@
|
|||
using Data.SeaHavenIndustries;
|
||||
|
||||
namespace SeaHaven.DataServices.Interfaces
|
||||
{
|
||||
public interface ITaskListTemplateDataService
|
||||
{
|
||||
Task<TaskListTemplate?> GetByIdAsync(int id);
|
||||
Task<TaskListTemplate?> GetByIdWithItemsAsync(int id);
|
||||
Task<IEnumerable<TaskListTemplate>> GetAllAsync();
|
||||
Task<TaskListTemplate> AddAsync(TaskListTemplate template);
|
||||
Task UpdateAsync(TaskListTemplate template);
|
||||
Task DeleteAsync(int id);
|
||||
Task<bool> ExistsAsync(int id);
|
||||
}
|
||||
}
|
||||
12
SeaHaven.DataServices/Interfaces/IUserDataService.cs
Normal file
12
SeaHaven.DataServices/Interfaces/IUserDataService.cs
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
using Data.SeaHavenIndustries;
|
||||
|
||||
namespace SeaHaven.DataServices.Interfaces
|
||||
{
|
||||
public interface IUserDataService
|
||||
{
|
||||
Task<ApplicationUser?> GetByIdAsync(string id);
|
||||
Task<IEnumerable<ApplicationUser>> GetAllAsync();
|
||||
Task<ApplicationUser?> GetByEmailAsync(string email);
|
||||
Task<bool> ExistsAsync(string id);
|
||||
}
|
||||
}
|
||||
|
|
@ -38,22 +38,35 @@ namespace SeaHaven.DataServices.Interfaces
|
|||
Task UpdateAsync(WorkOrder workOrder);
|
||||
Task DeleteAsync(int id);
|
||||
|
||||
// Related entities
|
||||
// Related entities - Read
|
||||
Task<IEnumerable<WorkOrderContacts>> GetWorkOrderContactsAsync(int workOrderId);
|
||||
Task<IEnumerable<WorkOrderCategories>> GetWorkOrderCategoriesAsync(int workOrderId);
|
||||
Task<IEnumerable<WorkOrderAttachments>> GetWorkOrderAttachmentsAsync(int workOrderId);
|
||||
Task<IEnumerable<Comments>> GetWorkOrderCommentsAsync(int workOrderId);
|
||||
|
||||
Task<IEnumerable<WorkOrderAuditLog>> GetWorkOrderAuditLogsAsync(int workOrderId);
|
||||
|
||||
// Related entities - Write
|
||||
Task AddWorkOrderContactsAsync(int workOrderId, IEnumerable<int> contactIds);
|
||||
Task AddWorkOrderCategoriesAsync(int workOrderId, IEnumerable<int> categoryIds);
|
||||
Task AddWorkOrderAttachmentAsync(WorkOrderAttachments attachment);
|
||||
Task AddAuditLogAsync(WorkOrderAuditLog auditLog);
|
||||
|
||||
// Related entities - Delete
|
||||
Task DeleteWorkOrderContactsAsync(int workOrderId);
|
||||
Task DeleteWorkOrderCategoriesAsync(int workOrderId);
|
||||
Task DeleteWorkOrderAttachmentsAsync(int workOrderId);
|
||||
|
||||
// Validation
|
||||
Task<bool> ExistsAsync(int id);
|
||||
Task<bool> InternalNumberExistsAsync(string internalNumber, int? excludeId = null);
|
||||
|
||||
|
||||
// Internal number generation
|
||||
Task<string> GenerateNextInternalWONumberAsync();
|
||||
|
||||
|
||||
// Statistics
|
||||
Task<int> CountAsync();
|
||||
Task<int> CountByStatusAsync(string status);
|
||||
Task<int> CountByLocationAsync(int locationId);
|
||||
Task<IEnumerable<WorkOrder>> GetAllWithDetailsAsync();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,8 +1,5 @@
|
|||
namespace SeaHaven.Services.DTOs
|
||||
{
|
||||
/// <summary>
|
||||
/// Contact DTO for responses
|
||||
/// </summary>
|
||||
public class ContactDTO
|
||||
{
|
||||
public int Id { get; set; }
|
||||
|
|
@ -21,9 +18,6 @@ namespace SeaHaven.Services.DTOs
|
|||
public string? ModifiedBy { get; set; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// DTO for creating a new contact
|
||||
/// </summary>
|
||||
public class CreateContactDTO
|
||||
{
|
||||
public required string FirstName { get; set; }
|
||||
|
|
@ -36,9 +30,6 @@ namespace SeaHaven.Services.DTOs
|
|||
public int? AccountId { get; set; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// DTO for updating an existing contact
|
||||
/// </summary>
|
||||
public class UpdateContactDTO
|
||||
{
|
||||
public string? FirstName { get; set; }
|
||||
|
|
@ -51,9 +42,6 @@ namespace SeaHaven.Services.DTOs
|
|||
public int? AccountId { get; set; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Paged result wrapper
|
||||
/// </summary>
|
||||
public class PagedResult<T>
|
||||
{
|
||||
public IEnumerable<T> Items { get; set; } = new List<T>();
|
||||
|
|
|
|||
|
|
@ -1,3 +1,6 @@
|
|||
using Data.SeaHavenIndustries;
|
||||
using Data.SeaHavenIndustries.Enums;
|
||||
|
||||
namespace SeaHaven.Services.DTOs
|
||||
{
|
||||
public class WorkOrderDTO
|
||||
|
|
@ -6,8 +9,8 @@ namespace SeaHaven.Services.DTOs
|
|||
public string? InternalWONumber { get; set; }
|
||||
public string? WorkerOrderTitle { get; set; }
|
||||
public string? Description { get; set; }
|
||||
public string? Status { get; set; }
|
||||
public string? Priority { get; set; }
|
||||
public WorkOrderStatus Status { get; set; }
|
||||
public WorkOrderPriority Priority { get; set; }
|
||||
public int? LocationId { get; set; }
|
||||
public string? LocationName { get; set; }
|
||||
public string? AssignTo { get; set; }
|
||||
|
|
@ -17,28 +20,110 @@ namespace SeaHaven.Services.DTOs
|
|||
public string? CreatedBy { get; set; }
|
||||
public DateTime? ModifiedDate { get; set; }
|
||||
public string? ModifiedBy { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Maps entity to DTO (Entity -> DTO)
|
||||
/// </summary>
|
||||
public static WorkOrderDTO MapToModel(WorkOrder entity)
|
||||
{
|
||||
if (entity == null) return null;
|
||||
|
||||
return new WorkOrderDTO
|
||||
{
|
||||
Id = entity.Id,
|
||||
InternalWONumber = entity.InternalWONumber,
|
||||
WorkerOrderTitle = entity.WorkerOrderTitle,
|
||||
Description = entity.Description,
|
||||
Status = Enum.TryParse<WorkOrderStatus>(entity.Status, out var status) ? status : WorkOrderStatus.Open,
|
||||
Priority = Enum.TryParse<WorkOrderPriority>(entity.Priority, out var priority) ? priority : WorkOrderPriority.Medium,
|
||||
LocationId = entity.LocationId,
|
||||
LocationName = null,
|
||||
AssignTo = entity.AssignTo,
|
||||
DueDate = entity.DueDate,
|
||||
CompletedDate = entity.CompletedDate,
|
||||
CreatedDate = entity.CreatedDate,
|
||||
CreatedBy = entity.createdby,
|
||||
ModifiedDate = entity.LastModificationTime,
|
||||
ModifiedBy = entity.LastModifierUserId?.ToString()
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Maps DTO to entity (DTO -> Entity)
|
||||
/// </summary>
|
||||
public static WorkOrder MapToEntity(WorkOrderDTO dto)
|
||||
{
|
||||
if (dto == null) return null;
|
||||
|
||||
return new WorkOrder
|
||||
{
|
||||
Id = dto.Id,
|
||||
InternalWONumber = dto.InternalWONumber,
|
||||
WorkerOrderTitle = dto.WorkerOrderTitle,
|
||||
Description = dto.Description,
|
||||
Status = dto.Status.ToString(),
|
||||
Priority = dto.Priority.ToString(),
|
||||
LocationId = dto.LocationId,
|
||||
AssignTo = dto.AssignTo,
|
||||
DueDate = dto.DueDate,
|
||||
CompletedDate = dto.CompletedDate
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
public class CreateWorkOrderDTO
|
||||
{
|
||||
public required string WorkerOrderTitle { get; set; }
|
||||
public string? Description { get; set; }
|
||||
public string? Status { get; set; }
|
||||
public string? Priority { get; set; }
|
||||
public WorkOrderStatus? Status { get; set; }
|
||||
public WorkOrderPriority? Priority { get; set; }
|
||||
public int? LocationId { get; set; }
|
||||
public string? AssignTo { get; set; }
|
||||
public DateTime? DueDate { get; set; }
|
||||
|
||||
public static WorkOrder MapToEntity(CreateWorkOrderDTO dto)
|
||||
{
|
||||
if (dto == null) return null!;
|
||||
|
||||
return new WorkOrder
|
||||
{
|
||||
WorkerOrderTitle = dto.WorkerOrderTitle,
|
||||
Description = dto.Description,
|
||||
Status = (dto.Status ?? WorkOrderStatus.Open).ToString(),
|
||||
Priority = (dto.Priority ?? WorkOrderPriority.Medium).ToString(),
|
||||
LocationId = dto.LocationId,
|
||||
AssignTo = dto.AssignTo,
|
||||
DueDate = dto.DueDate,
|
||||
CreatedDate = DateTime.UtcNow
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
public class UpdateWorkOrderDTO
|
||||
{
|
||||
public string? WorkerOrderTitle { get; set; }
|
||||
public string? Description { get; set; }
|
||||
public string? Status { get; set; }
|
||||
public string? Priority { get; set; }
|
||||
public WorkOrderStatus? Status { get; set; }
|
||||
public WorkOrderPriority? Priority { get; set; }
|
||||
public int? LocationId { get; set; }
|
||||
public string? AssignTo { get; set; }
|
||||
public DateTime? DueDate { get; set; }
|
||||
public DateTime? CompletedDate { get; set; }
|
||||
|
||||
public static void UpdateEntity(UpdateWorkOrderDTO dto, WorkOrder entity)
|
||||
{
|
||||
if (dto == null || entity == null) return;
|
||||
|
||||
if (dto.WorkerOrderTitle != null) entity.WorkerOrderTitle = dto.WorkerOrderTitle;
|
||||
if (dto.Description != null) entity.Description = dto.Description;
|
||||
if (dto.Status.HasValue) entity.Status = dto.Status.ToString()!;
|
||||
if (dto.Priority.HasValue) entity.Priority = dto.Priority.ToString()!;
|
||||
if (dto.LocationId.HasValue) entity.LocationId = dto.LocationId;
|
||||
if (dto.AssignTo != null) entity.AssignTo = dto.AssignTo;
|
||||
if (dto.DueDate.HasValue) entity.DueDate = dto.DueDate;
|
||||
if (dto.CompletedDate.HasValue) entity.CompletedDate = dto.CompletedDate;
|
||||
|
||||
entity.LastModificationTime = DateTime.UtcNow;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
38
SeaHaven.Services/DependencyInjection/ServicesModule.cs
Normal file
38
SeaHaven.Services/DependencyInjection/ServicesModule.cs
Normal file
|
|
@ -0,0 +1,38 @@
|
|||
using FluentValidation;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using System.Reflection;
|
||||
|
||||
namespace SeaHaven.Services.DependencyInjection
|
||||
{
|
||||
public static class ServicesModule
|
||||
{
|
||||
private static readonly List<Type> ExcludedTypes = new();
|
||||
|
||||
public static IServiceCollection AddBusinessServices(this IServiceCollection services)
|
||||
{
|
||||
var assembly = Assembly.GetExecutingAssembly();
|
||||
|
||||
var allClasses = assembly.GetTypes()
|
||||
.Where(t => t.IsClass &&
|
||||
!t.IsAbstract &&
|
||||
!t.IsGenericType &&
|
||||
!ExcludedTypes.Contains(t))
|
||||
.ToList();
|
||||
|
||||
foreach (var implementationType in allClasses)
|
||||
{
|
||||
var defaultInterface = implementationType.GetInterfaces()
|
||||
.FirstOrDefault(i => i.Name == $"I{implementationType.Name}");
|
||||
|
||||
if (defaultInterface != null)
|
||||
{
|
||||
services.AddScoped(defaultInterface, implementationType);
|
||||
}
|
||||
}
|
||||
|
||||
services.AddValidatorsFromAssembly(assembly);
|
||||
|
||||
return services;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,7 +1,9 @@
|
|||
using Data.SeaHavenIndustries;
|
||||
using FluentValidation;
|
||||
using SeaHaven.DataServices.Interfaces;
|
||||
using SeaHaven.Services.DTOs;
|
||||
using SeaHaven.Services.Interfaces;
|
||||
using SeaHaven.Services.Validation;
|
||||
|
||||
namespace SeaHaven.Services.Implementation
|
||||
{
|
||||
|
|
@ -12,10 +14,17 @@ namespace SeaHaven.Services.Implementation
|
|||
public class AccountService : IAccountService
|
||||
{
|
||||
private readonly IAccountDataService _accountDataService;
|
||||
private readonly ICreateAccountValidation _createValidator;
|
||||
private readonly IUpdateAccountValidation _updateValidator;
|
||||
|
||||
public AccountService(IAccountDataService accountDataService)
|
||||
public AccountService(
|
||||
IAccountDataService accountDataService,
|
||||
ICreateAccountValidation createValidator,
|
||||
IUpdateAccountValidation updateValidator)
|
||||
{
|
||||
_accountDataService = accountDataService;
|
||||
_createValidator = createValidator;
|
||||
_updateValidator = updateValidator;
|
||||
}
|
||||
|
||||
public async Task<AccountDTO?> GetAccountByIdAsync(int id)
|
||||
|
|
@ -50,6 +59,12 @@ namespace SeaHaven.Services.Implementation
|
|||
|
||||
public async Task<AccountDTO> CreateAccountAsync(CreateAccountDTO dto, string userId)
|
||||
{
|
||||
var validationResult = await _createValidator.ValidateAsync(dto);
|
||||
if (!validationResult.IsValid)
|
||||
{
|
||||
throw new ValidationException(validationResult.Errors);
|
||||
}
|
||||
|
||||
var account = new Accounts
|
||||
{
|
||||
Name = dto.Name,
|
||||
|
|
@ -66,6 +81,12 @@ namespace SeaHaven.Services.Implementation
|
|||
|
||||
public async Task<AccountDTO> UpdateAccountAsync(int id, UpdateAccountDTO dto, string userId)
|
||||
{
|
||||
var validationResult = await _updateValidator.ValidateAsync(dto);
|
||||
if (!validationResult.IsValid)
|
||||
{
|
||||
throw new ValidationException(validationResult.Errors);
|
||||
}
|
||||
|
||||
var account = await _accountDataService.GetByIdAsync(id);
|
||||
if (account == null)
|
||||
throw new InvalidOperationException($"Account with ID {id} not found");
|
||||
|
|
|
|||
|
|
@ -1,7 +1,9 @@
|
|||
using Data.SeaHavenIndustries;
|
||||
using FluentValidation;
|
||||
using SeaHaven.DataServices.Interfaces;
|
||||
using SeaHaven.Services.DTOs;
|
||||
using SeaHaven.Services.Interfaces;
|
||||
using SeaHaven.Services.Validation;
|
||||
|
||||
namespace SeaHaven.Services.Implementation
|
||||
{
|
||||
|
|
@ -12,10 +14,17 @@ namespace SeaHaven.Services.Implementation
|
|||
public class AssetService : IAssetService
|
||||
{
|
||||
private readonly IAssetDataService _assetDataService;
|
||||
private readonly ICreateAssetValidation _createValidator;
|
||||
private readonly IUpdateAssetValidation _updateValidator;
|
||||
|
||||
public AssetService(IAssetDataService assetDataService)
|
||||
public AssetService(
|
||||
IAssetDataService assetDataService,
|
||||
ICreateAssetValidation createValidator,
|
||||
IUpdateAssetValidation updateValidator)
|
||||
{
|
||||
_assetDataService = assetDataService;
|
||||
_createValidator = createValidator;
|
||||
_updateValidator = updateValidator;
|
||||
}
|
||||
|
||||
public async Task<AssetDTO?> GetAssetByIdAsync(int id)
|
||||
|
|
@ -56,6 +65,12 @@ namespace SeaHaven.Services.Implementation
|
|||
|
||||
public async Task<AssetDTO> CreateAssetAsync(CreateAssetDTO dto, string userId)
|
||||
{
|
||||
var validationResult = await _createValidator.ValidateAsync(dto);
|
||||
if (!validationResult.IsValid)
|
||||
{
|
||||
throw new ValidationException(validationResult.Errors);
|
||||
}
|
||||
|
||||
var asset = new Assets
|
||||
{
|
||||
Name = dto.Name,
|
||||
|
|
@ -70,6 +85,12 @@ namespace SeaHaven.Services.Implementation
|
|||
|
||||
public async Task<AssetDTO> UpdateAssetAsync(int id, UpdateAssetDTO dto, string userId)
|
||||
{
|
||||
var validationResult = await _updateValidator.ValidateAsync(dto);
|
||||
if (!validationResult.IsValid)
|
||||
{
|
||||
throw new ValidationException(validationResult.Errors);
|
||||
}
|
||||
|
||||
var asset = await _assetDataService.GetByIdAsync(id);
|
||||
if (asset == null)
|
||||
throw new InvalidOperationException($"Asset with ID {id} not found");
|
||||
|
|
|
|||
|
|
@ -1,7 +1,9 @@
|
|||
using Data.SeaHavenIndustries;
|
||||
using FluentValidation;
|
||||
using SeaHaven.DataServices.Interfaces;
|
||||
using SeaHaven.Services.DTOs;
|
||||
using SeaHaven.Services.Interfaces;
|
||||
using SeaHaven.Services.Validation;
|
||||
|
||||
namespace SeaHaven.Services.Implementation
|
||||
{
|
||||
|
|
@ -12,10 +14,17 @@ namespace SeaHaven.Services.Implementation
|
|||
public class ContactService : IContactService
|
||||
{
|
||||
private readonly IContactDataService _contactDataService;
|
||||
private readonly ICreateContactValidation _createValidator;
|
||||
private readonly IUpdateContactValidation _updateValidator;
|
||||
|
||||
public ContactService(IContactDataService contactDataService)
|
||||
public ContactService(
|
||||
IContactDataService contactDataService,
|
||||
ICreateContactValidation createValidator,
|
||||
IUpdateContactValidation updateValidator)
|
||||
{
|
||||
_contactDataService = contactDataService;
|
||||
_createValidator = createValidator;
|
||||
_updateValidator = updateValidator;
|
||||
}
|
||||
|
||||
// Query operations
|
||||
|
|
@ -76,20 +85,21 @@ namespace SeaHaven.Services.Implementation
|
|||
// Command operations
|
||||
public async Task<ContactDTO> CreateContactAsync(CreateContactDTO dto, string userId)
|
||||
{
|
||||
// Validation
|
||||
if (string.IsNullOrWhiteSpace(dto.Email))
|
||||
throw new ArgumentException("Email is required", nameof(dto.Email));
|
||||
var validationResult = await _createValidator.ValidateAsync(dto);
|
||||
if (!validationResult.IsValid)
|
||||
{
|
||||
throw new ValidationException(validationResult.Errors);
|
||||
}
|
||||
|
||||
if (await _contactDataService.EmailExistsAsync(dto.Email))
|
||||
throw new InvalidOperationException($"Email '{dto.Email}' already exists");
|
||||
|
||||
// Map DTO to entity
|
||||
var contact = new Contacts
|
||||
{
|
||||
FirstName = dto.FirstName,
|
||||
LastName = dto.LastName,
|
||||
Email = dto.Email,
|
||||
PhoneNumber = dto.Phone ?? dto.Mobile, // Contacts only has PhoneNumber, not separate Mobile
|
||||
PhoneNumber = dto.Phone ?? dto.Mobile,
|
||||
Title = dto.Title,
|
||||
ContactType = dto.Type,
|
||||
AccountId = dto.AccountId,
|
||||
|
|
@ -97,7 +107,6 @@ namespace SeaHaven.Services.Implementation
|
|||
CreatedDate = DateTime.Now
|
||||
};
|
||||
|
||||
// Save
|
||||
var savedContact = await _contactDataService.AddAsync(contact);
|
||||
|
||||
return MapToDTO(savedContact);
|
||||
|
|
@ -105,19 +114,22 @@ namespace SeaHaven.Services.Implementation
|
|||
|
||||
public async Task<ContactDTO> UpdateContactAsync(int id, UpdateContactDTO dto, string userId)
|
||||
{
|
||||
// Check if exists
|
||||
var validationResult = await _updateValidator.ValidateAsync(dto);
|
||||
if (!validationResult.IsValid)
|
||||
{
|
||||
throw new ValidationException(validationResult.Errors);
|
||||
}
|
||||
|
||||
var existingContact = await _contactDataService.GetByIdAsync(id);
|
||||
if (existingContact == null)
|
||||
throw new KeyNotFoundException($"Contact with ID {id} not found");
|
||||
|
||||
// Validate email uniqueness if changed
|
||||
if (!string.IsNullOrWhiteSpace(dto.Email) && dto.Email != existingContact.Email)
|
||||
{
|
||||
if (await _contactDataService.EmailExistsAsync(dto.Email, id))
|
||||
throw new InvalidOperationException($"Email '{dto.Email}' already exists");
|
||||
}
|
||||
|
||||
// Update fields
|
||||
if (!string.IsNullOrWhiteSpace(dto.FirstName))
|
||||
existingContact.FirstName = dto.FirstName;
|
||||
|
||||
|
|
@ -128,7 +140,7 @@ namespace SeaHaven.Services.Implementation
|
|||
existingContact.Email = dto.Email;
|
||||
|
||||
if (dto.Phone != null || dto.Mobile != null)
|
||||
existingContact.PhoneNumber = dto.Phone ?? dto.Mobile; // Contacts only has PhoneNumber
|
||||
existingContact.PhoneNumber = dto.Phone ?? dto.Mobile;
|
||||
|
||||
if (dto.Title != null)
|
||||
existingContact.Title = dto.Title;
|
||||
|
|
@ -141,7 +153,6 @@ namespace SeaHaven.Services.Implementation
|
|||
|
||||
existingContact.LastModificationTime = DateTime.Now;
|
||||
|
||||
// Save
|
||||
await _contactDataService.UpdateAsync(existingContact);
|
||||
|
||||
return MapToDTO(existingContact);
|
||||
|
|
|
|||
|
|
@ -1,7 +1,9 @@
|
|||
using Data.SeaHavenIndustries;
|
||||
using FluentValidation;
|
||||
using SeaHaven.DataServices.Interfaces;
|
||||
using SeaHaven.Services.DTOs;
|
||||
using SeaHaven.Services.Interfaces;
|
||||
using SeaHaven.Services.Validation;
|
||||
|
||||
namespace SeaHaven.Services.Implementation
|
||||
{
|
||||
|
|
@ -12,10 +14,17 @@ namespace SeaHaven.Services.Implementation
|
|||
public class DispatchService : IDispatchService
|
||||
{
|
||||
private readonly IDispatchDataService _dispatchDataService;
|
||||
private readonly ICreateDispatchValidation _createValidator;
|
||||
private readonly IUpdateDispatchValidation _updateValidator;
|
||||
|
||||
public DispatchService(IDispatchDataService dispatchDataService)
|
||||
public DispatchService(
|
||||
IDispatchDataService dispatchDataService,
|
||||
ICreateDispatchValidation createValidator,
|
||||
IUpdateDispatchValidation updateValidator)
|
||||
{
|
||||
_dispatchDataService = dispatchDataService;
|
||||
_createValidator = createValidator;
|
||||
_updateValidator = updateValidator;
|
||||
}
|
||||
|
||||
public async Task<DispatchDTO?> GetDispatchByIdAsync(int id)
|
||||
|
|
@ -56,6 +65,12 @@ namespace SeaHaven.Services.Implementation
|
|||
|
||||
public async Task<DispatchDTO> CreateDispatchAsync(CreateDispatchDTO dto, string userId)
|
||||
{
|
||||
var validationResult = await _createValidator.ValidateAsync(dto);
|
||||
if (!validationResult.IsValid)
|
||||
{
|
||||
throw new ValidationException(validationResult.Errors);
|
||||
}
|
||||
|
||||
var dispatchNumber = await GenerateNextDispatchNumberAsync();
|
||||
|
||||
var dispatch = new Dispatch
|
||||
|
|
@ -74,6 +89,12 @@ namespace SeaHaven.Services.Implementation
|
|||
|
||||
public async Task<DispatchDTO> UpdateDispatchAsync(int id, UpdateDispatchDTO dto, string userId)
|
||||
{
|
||||
var validationResult = await _updateValidator.ValidateAsync(dto);
|
||||
if (!validationResult.IsValid)
|
||||
{
|
||||
throw new ValidationException(validationResult.Errors);
|
||||
}
|
||||
|
||||
var dispatch = await _dispatchDataService.GetByIdAsync(id);
|
||||
if (dispatch == null)
|
||||
throw new InvalidOperationException($"Dispatch with ID {id} not found");
|
||||
|
|
|
|||
|
|
@ -1,7 +1,9 @@
|
|||
using Data.SeaHavenIndustries;
|
||||
using FluentValidation;
|
||||
using SeaHaven.DataServices.Interfaces;
|
||||
using SeaHaven.Services.DTOs;
|
||||
using SeaHaven.Services.Interfaces;
|
||||
using SeaHaven.Services.Validation;
|
||||
|
||||
namespace SeaHaven.Services.Implementation
|
||||
{
|
||||
|
|
@ -12,10 +14,17 @@ namespace SeaHaven.Services.Implementation
|
|||
public class EmployeeService : IEmployeeService
|
||||
{
|
||||
private readonly IEmployeeDataService _employeeDataService;
|
||||
private readonly ICreateEmployeeValidation _createValidator;
|
||||
private readonly IUpdateEmployeeValidation _updateValidator;
|
||||
|
||||
public EmployeeService(IEmployeeDataService employeeDataService)
|
||||
public EmployeeService(
|
||||
IEmployeeDataService employeeDataService,
|
||||
ICreateEmployeeValidation createValidator,
|
||||
IUpdateEmployeeValidation updateValidator)
|
||||
{
|
||||
_employeeDataService = employeeDataService;
|
||||
_createValidator = createValidator;
|
||||
_updateValidator = updateValidator;
|
||||
}
|
||||
|
||||
// Query operations
|
||||
|
|
@ -51,6 +60,12 @@ namespace SeaHaven.Services.Implementation
|
|||
|
||||
public async Task<EmployeeDTO> CreateEmployeeAsync(CreateEmployeeDTO dto, string userId)
|
||||
{
|
||||
var validationResult = await _createValidator.ValidateAsync(dto);
|
||||
if (!validationResult.IsValid)
|
||||
{
|
||||
throw new ValidationException(validationResult.Errors);
|
||||
}
|
||||
|
||||
var employee = new Employee
|
||||
{
|
||||
FirstName = dto.FirstName,
|
||||
|
|
@ -86,6 +101,12 @@ namespace SeaHaven.Services.Implementation
|
|||
|
||||
public async Task<EmployeeDTO> UpdateEmployeeAsync(int id, UpdateEmployeeDTO dto, string userId)
|
||||
{
|
||||
var validationResult = await _updateValidator.ValidateAsync(dto);
|
||||
if (!validationResult.IsValid)
|
||||
{
|
||||
throw new ValidationException(validationResult.Errors);
|
||||
}
|
||||
|
||||
var employee = await _employeeDataService.GetByIdAsync(id);
|
||||
if (employee == null)
|
||||
throw new InvalidOperationException($"Employee with ID {id} not found");
|
||||
|
|
|
|||
|
|
@ -1,7 +1,9 @@
|
|||
using Data.SeaHavenIndustries;
|
||||
using FluentValidation;
|
||||
using SeaHaven.DataServices.Interfaces;
|
||||
using SeaHaven.Services.DTOs;
|
||||
using SeaHaven.Services.Interfaces;
|
||||
using SeaHaven.Services.Validation;
|
||||
|
||||
namespace SeaHaven.Services.Implementation
|
||||
{
|
||||
|
|
@ -12,10 +14,17 @@ namespace SeaHaven.Services.Implementation
|
|||
public class FollowUpService : IFollowUpService
|
||||
{
|
||||
private readonly IFollowUpDataService _followUpDataService;
|
||||
private readonly ICreateFollowUpValidation _createValidator;
|
||||
private readonly IUpdateFollowUpValidation _updateValidator;
|
||||
|
||||
public FollowUpService(IFollowUpDataService followUpDataService)
|
||||
public FollowUpService(
|
||||
IFollowUpDataService followUpDataService,
|
||||
ICreateFollowUpValidation createValidator,
|
||||
IUpdateFollowUpValidation updateValidator)
|
||||
{
|
||||
_followUpDataService = followUpDataService;
|
||||
_createValidator = createValidator;
|
||||
_updateValidator = updateValidator;
|
||||
}
|
||||
|
||||
public async Task<FollowUpDTO?> GetFollowUpByIdAsync(int id)
|
||||
|
|
@ -63,6 +72,12 @@ namespace SeaHaven.Services.Implementation
|
|||
|
||||
public async Task<FollowUpDTO> CreateFollowUpAsync(CreateFollowUpDTO dto, string userId)
|
||||
{
|
||||
var validationResult = await _createValidator.ValidateAsync(dto);
|
||||
if (!validationResult.IsValid)
|
||||
{
|
||||
throw new ValidationException(validationResult.Errors);
|
||||
}
|
||||
|
||||
var followUp = new FollowUps
|
||||
{
|
||||
Reason = dto.Reason,
|
||||
|
|
@ -83,6 +98,12 @@ namespace SeaHaven.Services.Implementation
|
|||
|
||||
public async Task<FollowUpDTO> UpdateFollowUpAsync(int id, UpdateFollowUpDTO dto, string userId)
|
||||
{
|
||||
var validationResult = await _updateValidator.ValidateAsync(dto);
|
||||
if (!validationResult.IsValid)
|
||||
{
|
||||
throw new ValidationException(validationResult.Errors);
|
||||
}
|
||||
|
||||
var followUp = await _followUpDataService.GetByIdAsync(id);
|
||||
if (followUp == null)
|
||||
throw new InvalidOperationException($"Follow-Up with ID {id} not found");
|
||||
|
|
|
|||
|
|
@ -1,7 +1,9 @@
|
|||
using Data.SeaHavenIndustries;
|
||||
using FluentValidation;
|
||||
using SeaHaven.DataServices.Interfaces;
|
||||
using SeaHaven.Services.DTOs;
|
||||
using SeaHaven.Services.Interfaces;
|
||||
using SeaHaven.Services.Validation;
|
||||
|
||||
namespace SeaHaven.Services.Implementation
|
||||
{
|
||||
|
|
@ -12,10 +14,17 @@ namespace SeaHaven.Services.Implementation
|
|||
public class LocationService : ILocationService
|
||||
{
|
||||
private readonly ILocationDataService _locationDataService;
|
||||
private readonly ICreateLocationValidation _createValidator;
|
||||
private readonly IUpdateLocationValidation _updateValidator;
|
||||
|
||||
public LocationService(ILocationDataService locationDataService)
|
||||
public LocationService(
|
||||
ILocationDataService locationDataService,
|
||||
ICreateLocationValidation createValidator,
|
||||
IUpdateLocationValidation updateValidator)
|
||||
{
|
||||
_locationDataService = locationDataService;
|
||||
_createValidator = createValidator;
|
||||
_updateValidator = updateValidator;
|
||||
}
|
||||
|
||||
// Query operations
|
||||
|
|
@ -57,6 +66,12 @@ namespace SeaHaven.Services.Implementation
|
|||
|
||||
public async Task<LocationDTO> CreateLocationAsync(CreateLocationDTO dto, string userId)
|
||||
{
|
||||
var validationResult = await _createValidator.ValidateAsync(dto);
|
||||
if (!validationResult.IsValid)
|
||||
{
|
||||
throw new ValidationException(validationResult.Errors);
|
||||
}
|
||||
|
||||
var location = new Locations
|
||||
{
|
||||
Name = dto.LocationName,
|
||||
|
|
@ -75,6 +90,12 @@ namespace SeaHaven.Services.Implementation
|
|||
|
||||
public async Task<LocationDTO> UpdateLocationAsync(int id, UpdateLocationDTO dto, string userId)
|
||||
{
|
||||
var validationResult = await _updateValidator.ValidateAsync(dto);
|
||||
if (!validationResult.IsValid)
|
||||
{
|
||||
throw new ValidationException(validationResult.Errors);
|
||||
}
|
||||
|
||||
var location = await _locationDataService.GetByIdAsync(id);
|
||||
if (location == null)
|
||||
throw new InvalidOperationException($"Location with ID {id} not found");
|
||||
|
|
|
|||
|
|
@ -1,7 +1,9 @@
|
|||
using Data.SeaHavenIndustries;
|
||||
using FluentValidation;
|
||||
using SeaHaven.DataServices.Interfaces;
|
||||
using SeaHaven.Services.DTOs;
|
||||
using SeaHaven.Services.Interfaces;
|
||||
using SeaHaven.Services.Validation;
|
||||
|
||||
namespace SeaHaven.Services.Implementation
|
||||
{
|
||||
|
|
@ -12,10 +14,17 @@ namespace SeaHaven.Services.Implementation
|
|||
public class PMScheduleService : IPMScheduleService
|
||||
{
|
||||
private readonly IPMScheduleDataService _pmScheduleDataService;
|
||||
private readonly ICreatePMScheduleValidation _createValidator;
|
||||
private readonly IUpdatePMScheduleValidation _updateValidator;
|
||||
|
||||
public PMScheduleService(IPMScheduleDataService pmScheduleDataService)
|
||||
public PMScheduleService(
|
||||
IPMScheduleDataService pmScheduleDataService,
|
||||
ICreatePMScheduleValidation createValidator,
|
||||
IUpdatePMScheduleValidation updateValidator)
|
||||
{
|
||||
_pmScheduleDataService = pmScheduleDataService;
|
||||
_createValidator = createValidator;
|
||||
_updateValidator = updateValidator;
|
||||
}
|
||||
|
||||
public async Task<PMScheduleDTO?> GetPMScheduleByIdAsync(int id)
|
||||
|
|
@ -56,6 +65,12 @@ namespace SeaHaven.Services.Implementation
|
|||
|
||||
public async Task<PMScheduleDTO> CreatePMScheduleAsync(CreatePMScheduleDTO dto, string userId)
|
||||
{
|
||||
var validationResult = await _createValidator.ValidateAsync(dto);
|
||||
if (!validationResult.IsValid)
|
||||
{
|
||||
throw new ValidationException(validationResult.Errors);
|
||||
}
|
||||
|
||||
var pmSchedule = new PMSchedules
|
||||
{
|
||||
Name = dto.Name,
|
||||
|
|
@ -74,6 +89,12 @@ namespace SeaHaven.Services.Implementation
|
|||
|
||||
public async Task<PMScheduleDTO> UpdatePMScheduleAsync(int id, UpdatePMScheduleDTO dto, string userId)
|
||||
{
|
||||
var validationResult = await _updateValidator.ValidateAsync(dto);
|
||||
if (!validationResult.IsValid)
|
||||
{
|
||||
throw new ValidationException(validationResult.Errors);
|
||||
}
|
||||
|
||||
var pmSchedule = await _pmScheduleDataService.GetByIdAsync(id);
|
||||
if (pmSchedule == null)
|
||||
throw new InvalidOperationException($"PM Schedule with ID {id} not found");
|
||||
|
|
|
|||
|
|
@ -1,7 +1,9 @@
|
|||
using Data.SeaHavenIndustries;
|
||||
using FluentValidation;
|
||||
using SeaHaven.DataServices.Interfaces;
|
||||
using SeaHaven.Services.DTOs;
|
||||
using SeaHaven.Services.Interfaces;
|
||||
using SeaHaven.Services.Validation;
|
||||
|
||||
namespace SeaHaven.Services.Implementation
|
||||
{
|
||||
|
|
@ -12,10 +14,17 @@ namespace SeaHaven.Services.Implementation
|
|||
public class VendorService : IVendorService
|
||||
{
|
||||
private readonly IVendorDataService _vendorDataService;
|
||||
private readonly ICreateVendorValidation _createValidator;
|
||||
private readonly IUpdateVendorValidation _updateValidator;
|
||||
|
||||
public VendorService(IVendorDataService vendorDataService)
|
||||
public VendorService(
|
||||
IVendorDataService vendorDataService,
|
||||
ICreateVendorValidation createValidator,
|
||||
IUpdateVendorValidation updateValidator)
|
||||
{
|
||||
_vendorDataService = vendorDataService;
|
||||
_createValidator = createValidator;
|
||||
_updateValidator = updateValidator;
|
||||
}
|
||||
|
||||
public async Task<VendorDTO?> GetVendorByIdAsync(int id)
|
||||
|
|
@ -50,6 +59,12 @@ namespace SeaHaven.Services.Implementation
|
|||
|
||||
public async Task<VendorDTO> CreateVendorAsync(CreateVendorDTO dto, string userId)
|
||||
{
|
||||
var validationResult = await _createValidator.ValidateAsync(dto);
|
||||
if (!validationResult.IsValid)
|
||||
{
|
||||
throw new ValidationException(validationResult.Errors);
|
||||
}
|
||||
|
||||
var vendor = new Vendor
|
||||
{
|
||||
CompanyName = dto.Name,
|
||||
|
|
@ -69,6 +84,12 @@ namespace SeaHaven.Services.Implementation
|
|||
|
||||
public async Task<VendorDTO> UpdateVendorAsync(int id, UpdateVendorDTO dto, string userId)
|
||||
{
|
||||
var validationResult = await _updateValidator.ValidateAsync(dto);
|
||||
if (!validationResult.IsValid)
|
||||
{
|
||||
throw new ValidationException(validationResult.Errors);
|
||||
}
|
||||
|
||||
var vendor = await _vendorDataService.GetByIdAsync(id);
|
||||
if (vendor == null)
|
||||
throw new InvalidOperationException($"Vendor with ID {id} not found");
|
||||
|
|
|
|||
|
|
@ -1,35 +1,45 @@
|
|||
using Data.SeaHavenIndustries;
|
||||
using Data.SeaHavenIndustries.Enums;
|
||||
using FluentValidation;
|
||||
using SeaHaven.DataServices.Interfaces;
|
||||
using SeaHaven.Services.DTOs;
|
||||
using SeaHaven.Services.Interfaces;
|
||||
using SeaHaven.Services.Validation;
|
||||
|
||||
namespace SeaHaven.Services.Implementation
|
||||
{
|
||||
public class WorkOrderService : IWorkOrderService
|
||||
{
|
||||
private readonly IWorkOrderDataService _workOrderDataService;
|
||||
private readonly ICreateWorkOrderValidation _createValidator;
|
||||
private readonly IUpdateWorkOrderValidation _updateValidator;
|
||||
|
||||
public WorkOrderService(IWorkOrderDataService workOrderDataService)
|
||||
public WorkOrderService(
|
||||
IWorkOrderDataService workOrderDataService,
|
||||
ICreateWorkOrderValidation createValidator,
|
||||
IUpdateWorkOrderValidation updateValidator)
|
||||
{
|
||||
_workOrderDataService = workOrderDataService;
|
||||
_createValidator = createValidator;
|
||||
_updateValidator = updateValidator;
|
||||
}
|
||||
|
||||
public async Task<WorkOrderDTO?> GetWorkOrderByIdAsync(int id)
|
||||
{
|
||||
var workOrder = await _workOrderDataService.GetByIdAsync(id);
|
||||
return workOrder == null ? null : MapToDTO(workOrder);
|
||||
return workOrder == null ? null : WorkOrderDTO.MapToModel(workOrder);
|
||||
}
|
||||
|
||||
public async Task<WorkOrderDTO?> GetWorkOrderByIdWithDetailsAsync(int id)
|
||||
{
|
||||
var workOrder = await _workOrderDataService.GetByIdWithDetailsAsync(id);
|
||||
return workOrder == null ? null : MapToDTO(workOrder);
|
||||
return workOrder == null ? null : WorkOrderDTO.MapToModel(workOrder);
|
||||
}
|
||||
|
||||
public async Task<IEnumerable<WorkOrderDTO>> GetAllWorkOrdersAsync()
|
||||
{
|
||||
var workOrders = await _workOrderDataService.GetAllAsync();
|
||||
return workOrders.Select(MapToDTO);
|
||||
return workOrders.Select(WorkOrderDTO.MapToModel);
|
||||
}
|
||||
|
||||
public async Task<PagedResult<WorkOrderDTO>> GetWorkOrdersPagedAsync(int page, int pageSize, string? search = null, string? status = null, string? priority = null)
|
||||
|
|
@ -37,69 +47,71 @@ namespace SeaHaven.Services.Implementation
|
|||
var (items, totalCount) = await _workOrderDataService.GetPagedAsync(page, pageSize, search, status, priority);
|
||||
return new PagedResult<WorkOrderDTO>
|
||||
{
|
||||
Items = items.Select(MapToDTO),
|
||||
Items = items.Select(WorkOrderDTO.MapToModel),
|
||||
TotalCount = totalCount,
|
||||
Page = page,
|
||||
PageSize = pageSize
|
||||
};
|
||||
}
|
||||
|
||||
public async Task<WorkOrderDTO> CreateWorkOrderAsync(CreateWorkOrderDTO dto, string userId)
|
||||
public async Task<int> CreateWorkOrderAsync(CreateWorkOrderDTO dto, string userId)
|
||||
{
|
||||
var workOrder = new WorkOrder
|
||||
var validationResult = await _createValidator.ValidateAsync(dto);
|
||||
if (!validationResult.IsValid)
|
||||
{
|
||||
WorkerOrderTitle = dto.WorkerOrderTitle,
|
||||
Description = dto.Description,
|
||||
Status = dto.Status ?? "Open",
|
||||
Priority = dto.Priority,
|
||||
LocationId = dto.LocationId,
|
||||
AssignTo = dto.AssignTo,
|
||||
DueDate = dto.DueDate,
|
||||
createdby = userId,
|
||||
CreatedDate = DateTime.Now
|
||||
};
|
||||
throw new ValidationException(validationResult.Errors);
|
||||
}
|
||||
|
||||
// Generate internal number
|
||||
var workOrder = CreateWorkOrderDTO.MapToEntity(dto);
|
||||
workOrder.createdby = userId;
|
||||
workOrder.CreatedDate = DateTime.UtcNow;
|
||||
workOrder.InternalWONumber = await _workOrderDataService.GenerateNextInternalWONumberAsync();
|
||||
|
||||
var savedWorkOrder = await _workOrderDataService.AddAsync(workOrder);
|
||||
return MapToDTO(savedWorkOrder);
|
||||
var saved = await _workOrderDataService.AddAsync(workOrder);
|
||||
|
||||
await LogAuditAsync(saved.Id, userId, AuditAction.Create, "Work order created");
|
||||
|
||||
return saved.Id;
|
||||
}
|
||||
|
||||
public async Task<WorkOrderDTO> UpdateWorkOrderAsync(int id, UpdateWorkOrderDTO dto, string userId)
|
||||
public async Task<bool> UpdateWorkOrderAsync(int id, UpdateWorkOrderDTO dto, string userId)
|
||||
{
|
||||
var validationResult = await _updateValidator.ValidateAsync(dto);
|
||||
if (!validationResult.IsValid)
|
||||
{
|
||||
throw new ValidationException(validationResult.Errors);
|
||||
}
|
||||
|
||||
var existingWorkOrder = await _workOrderDataService.GetByIdAsync(id);
|
||||
if (existingWorkOrder == null)
|
||||
throw new KeyNotFoundException($"WorkOrder with ID {id} not found");
|
||||
return false;
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(dto.WorkerOrderTitle))
|
||||
existingWorkOrder.WorkerOrderTitle = dto.WorkerOrderTitle;
|
||||
if (dto.Description != null)
|
||||
existingWorkOrder.Description = dto.Description;
|
||||
if (dto.Status != null)
|
||||
existingWorkOrder.Status = dto.Status;
|
||||
if (dto.Priority != null)
|
||||
existingWorkOrder.Priority = dto.Priority;
|
||||
if (dto.LocationId.HasValue)
|
||||
existingWorkOrder.LocationId = dto.LocationId;
|
||||
if (dto.AssignTo != null)
|
||||
existingWorkOrder.AssignTo = dto.AssignTo;
|
||||
if (dto.DueDate.HasValue)
|
||||
existingWorkOrder.DueDate = dto.DueDate;
|
||||
if (dto.CompletedDate.HasValue)
|
||||
existingWorkOrder.CompletedDate = dto.CompletedDate;
|
||||
|
||||
existingWorkOrder.LastModificationTime = DateTime.Now;
|
||||
var changes = new List<string>();
|
||||
changes.AddRange(TrackChanges(existingWorkOrder, dto));
|
||||
|
||||
UpdateWorkOrderDTO.UpdateEntity(dto, existingWorkOrder);
|
||||
await _workOrderDataService.UpdateAsync(existingWorkOrder);
|
||||
return MapToDTO(existingWorkOrder);
|
||||
|
||||
if (changes.Any())
|
||||
{
|
||||
await LogAuditAsync(id, userId, AuditAction.Edit, string.Join(", ", changes));
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public async Task DeleteWorkOrderAsync(int id, string userId)
|
||||
public async Task<bool> DeleteWorkOrderAsync(int id, string userId)
|
||||
{
|
||||
if (!await _workOrderDataService.ExistsAsync(id))
|
||||
throw new KeyNotFoundException($"WorkOrder with ID {id} not found");
|
||||
return false;
|
||||
|
||||
await _workOrderDataService.DeleteWorkOrderAttachmentsAsync(id);
|
||||
await _workOrderDataService.DeleteWorkOrderContactsAsync(id);
|
||||
await _workOrderDataService.DeleteWorkOrderCategoriesAsync(id);
|
||||
await _workOrderDataService.DeleteAsync(id);
|
||||
|
||||
await LogAuditAsync(id, userId, AuditAction.Delete, "Work order deleted");
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public async Task<bool> WorkOrderExistsAsync(int id)
|
||||
|
|
@ -117,26 +129,40 @@ namespace SeaHaven.Services.Implementation
|
|||
return await _workOrderDataService.CountAsync();
|
||||
}
|
||||
|
||||
private static WorkOrderDTO MapToDTO(WorkOrder workOrder)
|
||||
private List<string> TrackChanges(WorkOrder existing, UpdateWorkOrderDTO dto)
|
||||
{
|
||||
return new WorkOrderDTO
|
||||
var changes = new List<string>();
|
||||
|
||||
if (dto.WorkerOrderTitle != null && dto.WorkerOrderTitle != existing.WorkerOrderTitle)
|
||||
changes.Add($"Title: {existing.WorkerOrderTitle} → {dto.WorkerOrderTitle}");
|
||||
|
||||
if (dto.Status.HasValue && dto.Status.ToString() != existing.Status)
|
||||
changes.Add($"Status: {existing.Status} → {dto.Status}");
|
||||
|
||||
if (dto.Priority.HasValue && dto.Priority.ToString() != existing.Priority)
|
||||
changes.Add($"Priority: {existing.Priority} → {dto.Priority}");
|
||||
|
||||
if (dto.AssignTo != null && dto.AssignTo != existing.AssignTo)
|
||||
changes.Add($"Assigned To: {existing.AssignTo} → {dto.AssignTo}");
|
||||
|
||||
if (dto.DueDate.HasValue && dto.DueDate != existing.DueDate)
|
||||
changes.Add($"Due Date: {existing.DueDate:yyyy-MM-dd} → {dto.DueDate:yyyy-MM-dd}");
|
||||
|
||||
return changes;
|
||||
}
|
||||
|
||||
private async Task LogAuditAsync(int workOrderId, string userId, AuditAction action, string description)
|
||||
{
|
||||
var auditLog = new WorkOrderAuditLog
|
||||
{
|
||||
Id = workOrder.Id,
|
||||
InternalWONumber = workOrder.InternalWONumber,
|
||||
WorkerOrderTitle = workOrder.WorkerOrderTitle,
|
||||
Description = workOrder.Description,
|
||||
Status = workOrder.Status,
|
||||
Priority = workOrder.Priority,
|
||||
LocationId = workOrder.LocationId,
|
||||
LocationName = workOrder.Locations?.Name,
|
||||
AssignTo = workOrder.AssignTo,
|
||||
DueDate = workOrder.DueDate,
|
||||
CompletedDate = workOrder.CompletedDate,
|
||||
CreatedDate = workOrder.CreatedDate,
|
||||
CreatedBy = workOrder.createdby,
|
||||
ModifiedDate = workOrder.LastModificationTime,
|
||||
ModifiedBy = null // Not tracked in FullAuditEntity
|
||||
WorkOrderId = workOrderId,
|
||||
Action = action.ToString(),
|
||||
FieldName = description,
|
||||
UserId = userId,
|
||||
CreatedAt = DateTime.UtcNow
|
||||
};
|
||||
|
||||
await _workOrderDataService.AddAuditLogAsync(auditLog);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -8,9 +8,9 @@ namespace SeaHaven.Services.Interfaces
|
|||
Task<WorkOrderDTO?> GetWorkOrderByIdWithDetailsAsync(int id);
|
||||
Task<IEnumerable<WorkOrderDTO>> GetAllWorkOrdersAsync();
|
||||
Task<PagedResult<WorkOrderDTO>> GetWorkOrdersPagedAsync(int page, int pageSize, string? search = null, string? status = null, string? priority = null);
|
||||
Task<WorkOrderDTO> CreateWorkOrderAsync(CreateWorkOrderDTO dto, string userId);
|
||||
Task<WorkOrderDTO> UpdateWorkOrderAsync(int id, UpdateWorkOrderDTO dto, string userId);
|
||||
Task DeleteWorkOrderAsync(int id, string userId);
|
||||
Task<int> CreateWorkOrderAsync(CreateWorkOrderDTO dto, string userId);
|
||||
Task<bool> UpdateWorkOrderAsync(int id, UpdateWorkOrderDTO dto, string userId);
|
||||
Task<bool> DeleteWorkOrderAsync(int id, string userId);
|
||||
Task<bool> WorkOrderExistsAsync(int id);
|
||||
Task<string> GenerateNextInternalWONumberAsync();
|
||||
Task<int> GetTotalWorkOrderCountAsync();
|
||||
|
|
|
|||
|
|
@ -11,4 +11,9 @@
|
|||
<ProjectReference Include="..\Data.SeaHavenIndustries\Data.SeaHavenIndustries.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="FluentValidation" Version="12.1.1" />
|
||||
<PackageReference Include="FluentValidation.DependencyInjectionExtensions" Version="12.1.1" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
|
|
|
|||
57
SeaHaven.Services/Validation/AccountValidation.cs
Normal file
57
SeaHaven.Services/Validation/AccountValidation.cs
Normal file
|
|
@ -0,0 +1,57 @@
|
|||
using FluentValidation;
|
||||
using SeaHaven.Services.DTOs;
|
||||
|
||||
namespace SeaHaven.Services.Validation
|
||||
{
|
||||
public interface ICreateAccountValidation : IValidator<CreateAccountDTO>
|
||||
{
|
||||
}
|
||||
|
||||
public class CreateAccountValidation : AbstractValidator<CreateAccountDTO>, ICreateAccountValidation
|
||||
{
|
||||
public CreateAccountValidation()
|
||||
{
|
||||
RuleFor(x => x.Name)
|
||||
.NotEmpty().WithMessage("Account name is required")
|
||||
.MaximumLength(200).WithMessage("Account name cannot exceed 200 characters");
|
||||
|
||||
RuleFor(x => x.Email)
|
||||
.EmailAddress().WithMessage("Invalid email address")
|
||||
.When(x => !string.IsNullOrEmpty(x.Email));
|
||||
|
||||
RuleFor(x => x.Phone)
|
||||
.MaximumLength(20).WithMessage("Phone number cannot exceed 20 characters")
|
||||
.When(x => !string.IsNullOrEmpty(x.Phone));
|
||||
|
||||
RuleFor(x => x.Website)
|
||||
.MaximumLength(200).WithMessage("Website cannot exceed 200 characters")
|
||||
.When(x => !string.IsNullOrEmpty(x.Website));
|
||||
}
|
||||
}
|
||||
|
||||
public interface IUpdateAccountValidation : IValidator<UpdateAccountDTO>
|
||||
{
|
||||
}
|
||||
|
||||
public class UpdateAccountValidation : AbstractValidator<UpdateAccountDTO>, IUpdateAccountValidation
|
||||
{
|
||||
public UpdateAccountValidation()
|
||||
{
|
||||
RuleFor(x => x.Name)
|
||||
.MaximumLength(200).WithMessage("Account name cannot exceed 200 characters")
|
||||
.When(x => !string.IsNullOrEmpty(x.Name));
|
||||
|
||||
RuleFor(x => x.Email)
|
||||
.EmailAddress().WithMessage("Invalid email address")
|
||||
.When(x => !string.IsNullOrEmpty(x.Email));
|
||||
|
||||
RuleFor(x => x.Phone)
|
||||
.MaximumLength(20).WithMessage("Phone number cannot exceed 20 characters")
|
||||
.When(x => !string.IsNullOrEmpty(x.Phone));
|
||||
|
||||
RuleFor(x => x.Website)
|
||||
.MaximumLength(200).WithMessage("Website cannot exceed 200 characters")
|
||||
.When(x => !string.IsNullOrEmpty(x.Website));
|
||||
}
|
||||
}
|
||||
}
|
||||
41
SeaHaven.Services/Validation/AssetValidation.cs
Normal file
41
SeaHaven.Services/Validation/AssetValidation.cs
Normal file
|
|
@ -0,0 +1,41 @@
|
|||
using FluentValidation;
|
||||
using SeaHaven.Services.DTOs;
|
||||
|
||||
namespace SeaHaven.Services.Validation
|
||||
{
|
||||
public interface ICreateAssetValidation : IValidator<CreateAssetDTO>
|
||||
{
|
||||
}
|
||||
|
||||
public class CreateAssetValidation : AbstractValidator<CreateAssetDTO>, ICreateAssetValidation
|
||||
{
|
||||
public CreateAssetValidation()
|
||||
{
|
||||
RuleFor(x => x.Name)
|
||||
.NotEmpty().WithMessage("Asset name is required")
|
||||
.MaximumLength(200).WithMessage("Asset name cannot exceed 200 characters");
|
||||
|
||||
RuleFor(x => x.LocationId)
|
||||
.GreaterThan(0).WithMessage("Valid location must be selected")
|
||||
.When(x => x.LocationId.HasValue);
|
||||
}
|
||||
}
|
||||
|
||||
public interface IUpdateAssetValidation : IValidator<UpdateAssetDTO>
|
||||
{
|
||||
}
|
||||
|
||||
public class UpdateAssetValidation : AbstractValidator<UpdateAssetDTO>, IUpdateAssetValidation
|
||||
{
|
||||
public UpdateAssetValidation()
|
||||
{
|
||||
RuleFor(x => x.Name)
|
||||
.MaximumLength(200).WithMessage("Asset name cannot exceed 200 characters")
|
||||
.When(x => !string.IsNullOrEmpty(x.Name));
|
||||
|
||||
RuleFor(x => x.LocationId)
|
||||
.GreaterThan(0).WithMessage("Valid location must be selected")
|
||||
.When(x => x.LocationId.HasValue);
|
||||
}
|
||||
}
|
||||
}
|
||||
73
SeaHaven.Services/Validation/ContactValidation.cs
Normal file
73
SeaHaven.Services/Validation/ContactValidation.cs
Normal file
|
|
@ -0,0 +1,73 @@
|
|||
using FluentValidation;
|
||||
using SeaHaven.Services.DTOs;
|
||||
|
||||
namespace SeaHaven.Services.Validation
|
||||
{
|
||||
public interface ICreateContactValidation : IValidator<CreateContactDTO>
|
||||
{
|
||||
}
|
||||
|
||||
public class CreateContactValidation : AbstractValidator<CreateContactDTO>, ICreateContactValidation
|
||||
{
|
||||
public CreateContactValidation()
|
||||
{
|
||||
RuleFor(x => x.FirstName)
|
||||
.NotEmpty().WithMessage("First name is required")
|
||||
.MaximumLength(100).WithMessage("First name cannot exceed 100 characters");
|
||||
|
||||
RuleFor(x => x.LastName)
|
||||
.NotEmpty().WithMessage("Last name is required")
|
||||
.MaximumLength(100).WithMessage("Last name cannot exceed 100 characters");
|
||||
|
||||
RuleFor(x => x.Email)
|
||||
.NotEmpty().WithMessage("Email is required")
|
||||
.EmailAddress().WithMessage("Invalid email address");
|
||||
|
||||
RuleFor(x => x.Phone)
|
||||
.MaximumLength(20).WithMessage("Phone number cannot exceed 20 characters")
|
||||
.When(x => !string.IsNullOrEmpty(x.Phone));
|
||||
|
||||
RuleFor(x => x.Mobile)
|
||||
.MaximumLength(20).WithMessage("Mobile number cannot exceed 20 characters")
|
||||
.When(x => !string.IsNullOrEmpty(x.Mobile));
|
||||
|
||||
RuleFor(x => x.AccountId)
|
||||
.GreaterThan(0).WithMessage("Valid account must be selected")
|
||||
.When(x => x.AccountId.HasValue);
|
||||
}
|
||||
}
|
||||
|
||||
public interface IUpdateContactValidation : IValidator<UpdateContactDTO>
|
||||
{
|
||||
}
|
||||
|
||||
public class UpdateContactValidation : AbstractValidator<UpdateContactDTO>, IUpdateContactValidation
|
||||
{
|
||||
public UpdateContactValidation()
|
||||
{
|
||||
RuleFor(x => x.FirstName)
|
||||
.MaximumLength(100).WithMessage("First name cannot exceed 100 characters")
|
||||
.When(x => !string.IsNullOrEmpty(x.FirstName));
|
||||
|
||||
RuleFor(x => x.LastName)
|
||||
.MaximumLength(100).WithMessage("Last name cannot exceed 100 characters")
|
||||
.When(x => !string.IsNullOrEmpty(x.LastName));
|
||||
|
||||
RuleFor(x => x.Email)
|
||||
.EmailAddress().WithMessage("Invalid email address")
|
||||
.When(x => !string.IsNullOrEmpty(x.Email));
|
||||
|
||||
RuleFor(x => x.Phone)
|
||||
.MaximumLength(20).WithMessage("Phone number cannot exceed 20 characters")
|
||||
.When(x => !string.IsNullOrEmpty(x.Phone));
|
||||
|
||||
RuleFor(x => x.Mobile)
|
||||
.MaximumLength(20).WithMessage("Mobile number cannot exceed 20 characters")
|
||||
.When(x => !string.IsNullOrEmpty(x.Mobile));
|
||||
|
||||
RuleFor(x => x.AccountId)
|
||||
.GreaterThan(0).WithMessage("Valid account must be selected")
|
||||
.When(x => x.AccountId.HasValue);
|
||||
}
|
||||
}
|
||||
}
|
||||
44
SeaHaven.Services/Validation/DispatchValidation.cs
Normal file
44
SeaHaven.Services/Validation/DispatchValidation.cs
Normal file
|
|
@ -0,0 +1,44 @@
|
|||
using FluentValidation;
|
||||
using SeaHaven.Services.DTOs;
|
||||
using Data.SeaHavenIndustries.Enums;
|
||||
|
||||
namespace SeaHaven.Services.Validation
|
||||
{
|
||||
public interface ICreateDispatchValidation : IValidator<CreateDispatchDTO>
|
||||
{
|
||||
}
|
||||
|
||||
public class CreateDispatchValidation : AbstractValidator<CreateDispatchDTO>, ICreateDispatchValidation
|
||||
{
|
||||
public CreateDispatchValidation()
|
||||
{
|
||||
RuleFor(x => x.VendorId)
|
||||
.NotEmpty().WithMessage("Vendor is required")
|
||||
.GreaterThan(0).WithMessage("Valid vendor must be selected");
|
||||
|
||||
RuleFor(x => x.ScheduledDate)
|
||||
.GreaterThan(DateTime.Now.AddDays(-1))
|
||||
.When(x => x.ScheduledDate.HasValue)
|
||||
.WithMessage("Scheduled date cannot be in the past");
|
||||
}
|
||||
}
|
||||
|
||||
public interface IUpdateDispatchValidation : IValidator<UpdateDispatchDTO>
|
||||
{
|
||||
}
|
||||
|
||||
public class UpdateDispatchValidation : AbstractValidator<UpdateDispatchDTO>, IUpdateDispatchValidation
|
||||
{
|
||||
public UpdateDispatchValidation()
|
||||
{
|
||||
RuleFor(x => x.VendorId)
|
||||
.GreaterThan(0).WithMessage("Valid vendor must be selected")
|
||||
.When(x => x.VendorId.HasValue);
|
||||
|
||||
RuleFor(x => x.ScheduledDate)
|
||||
.GreaterThan(DateTime.Now.AddDays(-1))
|
||||
.When(x => x.ScheduledDate.HasValue)
|
||||
.WithMessage("Scheduled date cannot be in the past");
|
||||
}
|
||||
}
|
||||
}
|
||||
81
SeaHaven.Services/Validation/EmployeeValidation.cs
Normal file
81
SeaHaven.Services/Validation/EmployeeValidation.cs
Normal file
|
|
@ -0,0 +1,81 @@
|
|||
using FluentValidation;
|
||||
using SeaHaven.Services.DTOs;
|
||||
|
||||
namespace SeaHaven.Services.Validation
|
||||
{
|
||||
public interface ICreateEmployeeValidation : IValidator<CreateEmployeeDTO>
|
||||
{
|
||||
}
|
||||
|
||||
public class CreateEmployeeValidation : AbstractValidator<CreateEmployeeDTO>, ICreateEmployeeValidation
|
||||
{
|
||||
public CreateEmployeeValidation()
|
||||
{
|
||||
RuleFor(x => x.FirstName)
|
||||
.NotEmpty().WithMessage("First name is required")
|
||||
.MaximumLength(100).WithMessage("First name cannot exceed 100 characters");
|
||||
|
||||
RuleFor(x => x.LastName)
|
||||
.NotEmpty().WithMessage("Last name is required")
|
||||
.MaximumLength(100).WithMessage("Last name cannot exceed 100 characters");
|
||||
|
||||
RuleFor(x => x.Email)
|
||||
.EmailAddress().WithMessage("Invalid email address")
|
||||
.When(x => !string.IsNullOrEmpty(x.Email));
|
||||
|
||||
RuleFor(x => x.Phone)
|
||||
.MaximumLength(20).WithMessage("Phone number cannot exceed 20 characters")
|
||||
.When(x => !string.IsNullOrEmpty(x.Phone));
|
||||
|
||||
RuleFor(x => x.DepartmentId)
|
||||
.GreaterThan(0).WithMessage("Valid department must be selected")
|
||||
.When(x => x.DepartmentId.HasValue);
|
||||
|
||||
RuleFor(x => x.JobTitleId)
|
||||
.GreaterThan(0).WithMessage("Valid job title must be selected")
|
||||
.When(x => x.JobTitleId.HasValue);
|
||||
|
||||
RuleFor(x => x.RegionId)
|
||||
.GreaterThan(0).WithMessage("Valid region must be selected")
|
||||
.When(x => x.RegionId.HasValue);
|
||||
}
|
||||
}
|
||||
|
||||
public interface IUpdateEmployeeValidation : IValidator<UpdateEmployeeDTO>
|
||||
{
|
||||
}
|
||||
|
||||
public class UpdateEmployeeValidation : AbstractValidator<UpdateEmployeeDTO>, IUpdateEmployeeValidation
|
||||
{
|
||||
public UpdateEmployeeValidation()
|
||||
{
|
||||
RuleFor(x => x.FirstName)
|
||||
.MaximumLength(100).WithMessage("First name cannot exceed 100 characters")
|
||||
.When(x => !string.IsNullOrEmpty(x.FirstName));
|
||||
|
||||
RuleFor(x => x.LastName)
|
||||
.MaximumLength(100).WithMessage("Last name cannot exceed 100 characters")
|
||||
.When(x => !string.IsNullOrEmpty(x.LastName));
|
||||
|
||||
RuleFor(x => x.Email)
|
||||
.EmailAddress().WithMessage("Invalid email address")
|
||||
.When(x => !string.IsNullOrEmpty(x.Email));
|
||||
|
||||
RuleFor(x => x.Phone)
|
||||
.MaximumLength(20).WithMessage("Phone number cannot exceed 20 characters")
|
||||
.When(x => !string.IsNullOrEmpty(x.Phone));
|
||||
|
||||
RuleFor(x => x.DepartmentId)
|
||||
.GreaterThan(0).WithMessage("Valid department must be selected")
|
||||
.When(x => x.DepartmentId.HasValue);
|
||||
|
||||
RuleFor(x => x.JobTitleId)
|
||||
.GreaterThan(0).WithMessage("Valid job title must be selected")
|
||||
.When(x => x.JobTitleId.HasValue);
|
||||
|
||||
RuleFor(x => x.RegionId)
|
||||
.GreaterThan(0).WithMessage("Valid region must be selected")
|
||||
.When(x => x.RegionId.HasValue);
|
||||
}
|
||||
}
|
||||
}
|
||||
78
SeaHaven.Services/Validation/FollowUpValidation.cs
Normal file
78
SeaHaven.Services/Validation/FollowUpValidation.cs
Normal file
|
|
@ -0,0 +1,78 @@
|
|||
using FluentValidation;
|
||||
using SeaHaven.Services.DTOs;
|
||||
using Data.SeaHavenIndustries.Enums;
|
||||
|
||||
namespace SeaHaven.Services.Validation
|
||||
{
|
||||
public interface ICreateFollowUpValidation : IValidator<CreateFollowUpDTO>
|
||||
{
|
||||
}
|
||||
|
||||
public class CreateFollowUpValidation : AbstractValidator<CreateFollowUpDTO>, ICreateFollowUpValidation
|
||||
{
|
||||
public CreateFollowUpValidation()
|
||||
{
|
||||
RuleFor(x => x.Reason)
|
||||
.NotEmpty().WithMessage("Reason is required")
|
||||
.MaximumLength(500).WithMessage("Reason cannot exceed 500 characters");
|
||||
|
||||
RuleFor(x => x.Status)
|
||||
.NotEmpty().WithMessage("Status is required");
|
||||
|
||||
RuleFor(x => x.ScheduleDate)
|
||||
.GreaterThan(DateTime.Now.AddDays(-1))
|
||||
.WithMessage("Schedule date cannot be in the past");
|
||||
|
||||
RuleFor(x => x.EmployeeId)
|
||||
.GreaterThan(0).WithMessage("Valid employee must be selected")
|
||||
.When(x => x.EmployeeId.HasValue);
|
||||
|
||||
RuleFor(x => x.WorkOrderId)
|
||||
.GreaterThan(0).WithMessage("Valid work order must be selected")
|
||||
.When(x => x.WorkOrderId.HasValue);
|
||||
|
||||
RuleFor(x => x.AccountId)
|
||||
.GreaterThan(0).WithMessage("Valid account must be selected")
|
||||
.When(x => x.AccountId.HasValue);
|
||||
|
||||
RuleFor(x => x.LocationId)
|
||||
.GreaterThan(0).WithMessage("Valid location must be selected")
|
||||
.When(x => x.LocationId.HasValue);
|
||||
}
|
||||
}
|
||||
|
||||
public interface IUpdateFollowUpValidation : IValidator<UpdateFollowUpDTO>
|
||||
{
|
||||
}
|
||||
|
||||
public class UpdateFollowUpValidation : AbstractValidator<UpdateFollowUpDTO>, IUpdateFollowUpValidation
|
||||
{
|
||||
public UpdateFollowUpValidation()
|
||||
{
|
||||
RuleFor(x => x.Reason)
|
||||
.MaximumLength(500).WithMessage("Reason cannot exceed 500 characters")
|
||||
.When(x => !string.IsNullOrEmpty(x.Reason));
|
||||
|
||||
RuleFor(x => x.ScheduleDate)
|
||||
.GreaterThan(DateTime.Now.AddDays(-1))
|
||||
.When(x => x.ScheduleDate.HasValue)
|
||||
.WithMessage("Schedule date cannot be in the past");
|
||||
|
||||
RuleFor(x => x.EmployeeId)
|
||||
.GreaterThan(0).WithMessage("Valid employee must be selected")
|
||||
.When(x => x.EmployeeId.HasValue);
|
||||
|
||||
RuleFor(x => x.WorkOrderId)
|
||||
.GreaterThan(0).WithMessage("Valid work order must be selected")
|
||||
.When(x => x.WorkOrderId.HasValue);
|
||||
|
||||
RuleFor(x => x.AccountId)
|
||||
.GreaterThan(0).WithMessage("Valid account must be selected")
|
||||
.When(x => x.AccountId.HasValue);
|
||||
|
||||
RuleFor(x => x.LocationId)
|
||||
.GreaterThan(0).WithMessage("Valid location must be selected")
|
||||
.When(x => x.LocationId.HasValue);
|
||||
}
|
||||
}
|
||||
}
|
||||
73
SeaHaven.Services/Validation/LocationValidation.cs
Normal file
73
SeaHaven.Services/Validation/LocationValidation.cs
Normal file
|
|
@ -0,0 +1,73 @@
|
|||
using FluentValidation;
|
||||
using SeaHaven.Services.DTOs;
|
||||
|
||||
namespace SeaHaven.Services.Validation
|
||||
{
|
||||
public interface ICreateLocationValidation : IValidator<CreateLocationDTO>
|
||||
{
|
||||
}
|
||||
|
||||
public class CreateLocationValidation : AbstractValidator<CreateLocationDTO>, ICreateLocationValidation
|
||||
{
|
||||
public CreateLocationValidation()
|
||||
{
|
||||
RuleFor(x => x.LocationName)
|
||||
.NotEmpty().WithMessage("Location name is required")
|
||||
.MaximumLength(200).WithMessage("Location name cannot exceed 200 characters");
|
||||
|
||||
RuleFor(x => x.Address)
|
||||
.MaximumLength(500).WithMessage("Address cannot exceed 500 characters")
|
||||
.When(x => !string.IsNullOrEmpty(x.Address));
|
||||
|
||||
RuleFor(x => x.City)
|
||||
.MaximumLength(100).WithMessage("City cannot exceed 100 characters")
|
||||
.When(x => !string.IsNullOrEmpty(x.City));
|
||||
|
||||
RuleFor(x => x.State)
|
||||
.MaximumLength(2).WithMessage("State must be 2 characters")
|
||||
.When(x => !string.IsNullOrEmpty(x.State));
|
||||
|
||||
RuleFor(x => x.Zipcode)
|
||||
.MaximumLength(10).WithMessage("Zipcode cannot exceed 10 characters")
|
||||
.When(x => !string.IsNullOrEmpty(x.Zipcode));
|
||||
|
||||
RuleFor(x => x.AccountId)
|
||||
.GreaterThan(0).WithMessage("Valid account must be selected")
|
||||
.When(x => x.AccountId.HasValue);
|
||||
}
|
||||
}
|
||||
|
||||
public interface IUpdateLocationValidation : IValidator<UpdateLocationDTO>
|
||||
{
|
||||
}
|
||||
|
||||
public class UpdateLocationValidation : AbstractValidator<UpdateLocationDTO>, IUpdateLocationValidation
|
||||
{
|
||||
public UpdateLocationValidation()
|
||||
{
|
||||
RuleFor(x => x.LocationName)
|
||||
.MaximumLength(200).WithMessage("Location name cannot exceed 200 characters")
|
||||
.When(x => !string.IsNullOrEmpty(x.LocationName));
|
||||
|
||||
RuleFor(x => x.Address)
|
||||
.MaximumLength(500).WithMessage("Address cannot exceed 500 characters")
|
||||
.When(x => !string.IsNullOrEmpty(x.Address));
|
||||
|
||||
RuleFor(x => x.City)
|
||||
.MaximumLength(100).WithMessage("City cannot exceed 100 characters")
|
||||
.When(x => !string.IsNullOrEmpty(x.City));
|
||||
|
||||
RuleFor(x => x.State)
|
||||
.MaximumLength(2).WithMessage("State must be 2 characters")
|
||||
.When(x => !string.IsNullOrEmpty(x.State));
|
||||
|
||||
RuleFor(x => x.Zipcode)
|
||||
.MaximumLength(10).WithMessage("Zipcode cannot exceed 10 characters")
|
||||
.When(x => !string.IsNullOrEmpty(x.Zipcode));
|
||||
|
||||
RuleFor(x => x.AccountId)
|
||||
.GreaterThan(0).WithMessage("Valid account must be selected")
|
||||
.When(x => x.AccountId.HasValue);
|
||||
}
|
||||
}
|
||||
}
|
||||
62
SeaHaven.Services/Validation/PMScheduleValidation.cs
Normal file
62
SeaHaven.Services/Validation/PMScheduleValidation.cs
Normal file
|
|
@ -0,0 +1,62 @@
|
|||
using FluentValidation;
|
||||
using SeaHaven.Services.DTOs;
|
||||
|
||||
namespace SeaHaven.Services.Validation
|
||||
{
|
||||
public interface ICreatePMScheduleValidation : IValidator<CreatePMScheduleDTO>
|
||||
{
|
||||
}
|
||||
|
||||
public class CreatePMScheduleValidation : AbstractValidator<CreatePMScheduleDTO>, ICreatePMScheduleValidation
|
||||
{
|
||||
public CreatePMScheduleValidation()
|
||||
{
|
||||
RuleFor(x => x.Name)
|
||||
.NotEmpty().WithMessage("PM Schedule name is required")
|
||||
.MaximumLength(200).WithMessage("Name cannot exceed 200 characters");
|
||||
|
||||
RuleFor(x => x.Frequency)
|
||||
.NotEmpty().WithMessage("Frequency is required");
|
||||
|
||||
RuleFor(x => x.StartDate)
|
||||
.NotEmpty().WithMessage("Start date is required")
|
||||
.LessThanOrEqualTo(DateTime.Now.AddYears(1))
|
||||
.WithMessage("Start date cannot be more than 1 year in the future");
|
||||
|
||||
RuleFor(x => x.AssetId)
|
||||
.GreaterThan(0).WithMessage("Valid asset must be selected")
|
||||
.When(x => x.AssetId.HasValue);
|
||||
|
||||
RuleFor(x => x.LocationId)
|
||||
.GreaterThan(0).WithMessage("Valid location must be selected")
|
||||
.When(x => x.LocationId.HasValue);
|
||||
}
|
||||
}
|
||||
|
||||
public interface IUpdatePMScheduleValidation : IValidator<UpdatePMScheduleDTO>
|
||||
{
|
||||
}
|
||||
|
||||
public class UpdatePMScheduleValidation : AbstractValidator<UpdatePMScheduleDTO>, IUpdatePMScheduleValidation
|
||||
{
|
||||
public UpdatePMScheduleValidation()
|
||||
{
|
||||
RuleFor(x => x.Name)
|
||||
.MaximumLength(200).WithMessage("Name cannot exceed 200 characters")
|
||||
.When(x => !string.IsNullOrEmpty(x.Name));
|
||||
|
||||
RuleFor(x => x.StartDate)
|
||||
.LessThanOrEqualTo(DateTime.Now.AddYears(1))
|
||||
.When(x => x.StartDate.HasValue)
|
||||
.WithMessage("Start date cannot be more than 1 year in the future");
|
||||
|
||||
RuleFor(x => x.AssetId)
|
||||
.GreaterThan(0).WithMessage("Valid asset must be selected")
|
||||
.When(x => x.AssetId.HasValue);
|
||||
|
||||
RuleFor(x => x.LocationId)
|
||||
.GreaterThan(0).WithMessage("Valid location must be selected")
|
||||
.When(x => x.LocationId.HasValue);
|
||||
}
|
||||
}
|
||||
}
|
||||
81
SeaHaven.Services/Validation/VendorValidation.cs
Normal file
81
SeaHaven.Services/Validation/VendorValidation.cs
Normal file
|
|
@ -0,0 +1,81 @@
|
|||
using FluentValidation;
|
||||
using SeaHaven.Services.DTOs;
|
||||
|
||||
namespace SeaHaven.Services.Validation
|
||||
{
|
||||
public interface ICreateVendorValidation : IValidator<CreateVendorDTO>
|
||||
{
|
||||
}
|
||||
|
||||
public class CreateVendorValidation : AbstractValidator<CreateVendorDTO>, ICreateVendorValidation
|
||||
{
|
||||
public CreateVendorValidation()
|
||||
{
|
||||
RuleFor(x => x.Name)
|
||||
.NotEmpty().WithMessage("Vendor name is required")
|
||||
.MaximumLength(200).WithMessage("Vendor name cannot exceed 200 characters");
|
||||
|
||||
RuleFor(x => x.Email)
|
||||
.EmailAddress().WithMessage("Invalid email address")
|
||||
.When(x => !string.IsNullOrEmpty(x.Email));
|
||||
|
||||
RuleFor(x => x.Phone)
|
||||
.MaximumLength(20).WithMessage("Phone number cannot exceed 20 characters")
|
||||
.When(x => !string.IsNullOrEmpty(x.Phone));
|
||||
|
||||
RuleFor(x => x.Address)
|
||||
.MaximumLength(500).WithMessage("Address cannot exceed 500 characters")
|
||||
.When(x => !string.IsNullOrEmpty(x.Address));
|
||||
|
||||
RuleFor(x => x.City)
|
||||
.MaximumLength(100).WithMessage("City cannot exceed 100 characters")
|
||||
.When(x => !string.IsNullOrEmpty(x.City));
|
||||
|
||||
RuleFor(x => x.State)
|
||||
.MaximumLength(2).WithMessage("State must be 2 characters")
|
||||
.When(x => !string.IsNullOrEmpty(x.State));
|
||||
|
||||
RuleFor(x => x.Zipcode)
|
||||
.MaximumLength(10).WithMessage("Zipcode cannot exceed 10 characters")
|
||||
.When(x => !string.IsNullOrEmpty(x.Zipcode));
|
||||
}
|
||||
}
|
||||
|
||||
public interface IUpdateVendorValidation : IValidator<UpdateVendorDTO>
|
||||
{
|
||||
}
|
||||
|
||||
public class UpdateVendorValidation : AbstractValidator<UpdateVendorDTO>, IUpdateVendorValidation
|
||||
{
|
||||
public UpdateVendorValidation()
|
||||
{
|
||||
RuleFor(x => x.Name)
|
||||
.MaximumLength(200).WithMessage("Vendor name cannot exceed 200 characters")
|
||||
.When(x => !string.IsNullOrEmpty(x.Name));
|
||||
|
||||
RuleFor(x => x.Email)
|
||||
.EmailAddress().WithMessage("Invalid email address")
|
||||
.When(x => !string.IsNullOrEmpty(x.Email));
|
||||
|
||||
RuleFor(x => x.Phone)
|
||||
.MaximumLength(20).WithMessage("Phone number cannot exceed 20 characters")
|
||||
.When(x => !string.IsNullOrEmpty(x.Phone));
|
||||
|
||||
RuleFor(x => x.Address)
|
||||
.MaximumLength(500).WithMessage("Address cannot exceed 500 characters")
|
||||
.When(x => !string.IsNullOrEmpty(x.Address));
|
||||
|
||||
RuleFor(x => x.City)
|
||||
.MaximumLength(100).WithMessage("City cannot exceed 100 characters")
|
||||
.When(x => !string.IsNullOrEmpty(x.City));
|
||||
|
||||
RuleFor(x => x.State)
|
||||
.MaximumLength(2).WithMessage("State must be 2 characters")
|
||||
.When(x => !string.IsNullOrEmpty(x.State));
|
||||
|
||||
RuleFor(x => x.Zipcode)
|
||||
.MaximumLength(10).WithMessage("Zipcode cannot exceed 10 characters")
|
||||
.When(x => !string.IsNullOrEmpty(x.Zipcode));
|
||||
}
|
||||
}
|
||||
}
|
||||
71
SeaHaven.Services/Validation/WorkOrderValidation.cs
Normal file
71
SeaHaven.Services/Validation/WorkOrderValidation.cs
Normal file
|
|
@ -0,0 +1,71 @@
|
|||
using FluentValidation;
|
||||
using SeaHaven.Services.DTOs;
|
||||
using Data.SeaHavenIndustries.Enums;
|
||||
|
||||
namespace SeaHaven.Services.Validation
|
||||
{
|
||||
public interface ICreateWorkOrderValidation : IValidator<CreateWorkOrderDTO>
|
||||
{
|
||||
}
|
||||
|
||||
public class CreateWorkOrderValidation : AbstractValidator<CreateWorkOrderDTO>, ICreateWorkOrderValidation
|
||||
{
|
||||
public CreateWorkOrderValidation()
|
||||
{
|
||||
RuleFor(x => x.WorkerOrderTitle)
|
||||
.NotEmpty().WithMessage("Work order title is required")
|
||||
.MaximumLength(200).WithMessage("Title cannot exceed 200 characters");
|
||||
|
||||
RuleFor(x => x.Description)
|
||||
.MaximumLength(2000).WithMessage("Description cannot exceed 2000 characters")
|
||||
.When(x => !string.IsNullOrEmpty(x.Description));
|
||||
|
||||
RuleFor(x => x.LocationId)
|
||||
.GreaterThan(0).WithMessage("Valid location must be selected")
|
||||
.When(x => x.LocationId.HasValue);
|
||||
|
||||
RuleFor(x => x.Priority)
|
||||
.IsInEnum().WithMessage("Invalid priority value")
|
||||
.When(x => x.Priority.HasValue);
|
||||
|
||||
RuleFor(x => x.Status)
|
||||
.IsInEnum().WithMessage("Invalid status value")
|
||||
.When(x => x.Status.HasValue);
|
||||
|
||||
RuleFor(x => x.DueDate)
|
||||
.GreaterThan(DateTime.Now.AddDays(-1))
|
||||
.When(x => x.DueDate.HasValue)
|
||||
.WithMessage("Due date cannot be in the past");
|
||||
}
|
||||
}
|
||||
|
||||
public interface IUpdateWorkOrderValidation : IValidator<UpdateWorkOrderDTO>
|
||||
{
|
||||
}
|
||||
|
||||
public class UpdateWorkOrderValidation : AbstractValidator<UpdateWorkOrderDTO>, IUpdateWorkOrderValidation
|
||||
{
|
||||
public UpdateWorkOrderValidation()
|
||||
{
|
||||
RuleFor(x => x.WorkerOrderTitle)
|
||||
.MaximumLength(200).WithMessage("Title cannot exceed 200 characters")
|
||||
.When(x => !string.IsNullOrEmpty(x.WorkerOrderTitle));
|
||||
|
||||
RuleFor(x => x.Description)
|
||||
.MaximumLength(2000).WithMessage("Description cannot exceed 2000 characters")
|
||||
.When(x => !string.IsNullOrEmpty(x.Description));
|
||||
|
||||
RuleFor(x => x.LocationId)
|
||||
.GreaterThan(0).WithMessage("Valid location must be selected")
|
||||
.When(x => x.LocationId.HasValue);
|
||||
|
||||
RuleFor(x => x.Priority)
|
||||
.IsInEnum().WithMessage("Invalid priority value")
|
||||
.When(x => x.Priority.HasValue);
|
||||
|
||||
RuleFor(x => x.Status)
|
||||
.IsInEnum().WithMessage("Invalid status value")
|
||||
.When(x => x.Status.HasValue);
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Reference in a new issue