mirror of
https://github.com/Sea-Haven-Industries/shoc-backend.git
synced 2026-10-03 10:23:28 +00:00
Compare commits
49 commits
19994ef725
...
5e4d9894e9
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5e4d9894e9 | ||
|
|
ac76b201de | ||
|
|
64adcae4ed | ||
|
|
06bde26aff | ||
|
|
899ee7b83d | ||
|
|
b36ccd2758 | ||
|
|
73be673444 | ||
|
|
6e0f891ca3 | ||
|
|
3355df111f | ||
|
|
575e8ab6fc | ||
|
|
81472bf729 | ||
|
|
c482fa0558 | ||
|
|
3a25fa8559 | ||
|
|
be190836a4 | ||
|
|
1f4bd12cd7 | ||
|
|
63f76e9b2c | ||
|
|
17590daf9d | ||
|
|
f89db3da52 | ||
|
|
7d764f966d | ||
|
|
091390fcad | ||
|
|
0a564499f5 | ||
|
|
7ef7205681 | ||
|
|
b072d1fa2d | ||
|
|
521bb0fba2 | ||
|
|
ee69a1863a | ||
|
|
d24c4643f5 | ||
|
|
05b36ef7dd | ||
|
|
78c7850fb2 | ||
|
|
8959efc06b | ||
|
|
d07e24e419 | ||
|
|
be70c84add | ||
|
|
18ca737307 | ||
|
|
e1f57d3fb2 | ||
|
|
c69cef7fa9 | ||
|
|
cc87039a77 | ||
|
|
5a4d8e2240 | ||
|
|
60fc933da7 | ||
|
|
6f8f67735a | ||
|
|
93fa76a5aa | ||
|
|
5a20e55138 | ||
|
|
534f97d6f1 | ||
|
|
51b52a7fb5 | ||
|
|
dbb31ca7d7 | ||
|
|
493cff00fb | ||
|
|
483b765d69 | ||
|
|
e8c400a20a | ||
|
|
128ae44a0d | ||
|
|
486233076c | ||
|
|
0e3cb761c9 |
164 changed files with 86061 additions and 769 deletions
3
.gitignore
vendored
3
.gitignore
vendored
|
|
@ -362,3 +362,6 @@ MigrationBackup/
|
|||
# Fody - auto-generated XML schema
|
||||
FodyWeavers.xsd
|
||||
/SeaHavenIndustries/wwwroot/ChromeEx/Win_x64_1000083_chrome-win/chrome-win
|
||||
|
||||
appsettings.Development.json
|
||||
.DS_Store
|
||||
|
|
|
|||
|
|
@ -7,7 +7,12 @@
|
|||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="AWSSDK.DynamoDBv2" Version="4.0.17.9" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="8.0.8" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="8.0.8">
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="SendGrid" Version="9.29.3" />
|
||||
<PackageReference Include="Swashbuckle.AspNetCore" Version="6.4.0" />
|
||||
<PackageReference Include="PasswordGenerator" Version="2.1.0" />
|
||||
|
|
@ -15,6 +20,8 @@
|
|||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Data.SeaHavenIndustries\Data.SeaHavenIndustries.csproj" />
|
||||
<ProjectReference Include="..\SeaHaven.Services\SeaHaven.Services.csproj" />
|
||||
<ProjectReference Include="..\SeaHaven.DataServices\SeaHaven.DataServices.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
|
|
|
|||
|
|
@ -2,16 +2,19 @@
|
|||
using Data.SeaHavenIndustries;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using SeaHaven.Services.Interfaces;
|
||||
using SeaHaven.Services.DTOs;
|
||||
|
||||
[ApiController]
|
||||
[Route("api/Account")]
|
||||
[Route("api/accounts")]
|
||||
public class AccountController : ControllerBase
|
||||
{
|
||||
private readonly ApplicationDbContext _db;
|
||||
private readonly IAccountService _accountService;
|
||||
|
||||
public AccountController(ApplicationDbContext db)
|
||||
public AccountController(IAccountService accountService)
|
||||
{
|
||||
_db = db;
|
||||
_accountService = accountService;
|
||||
}
|
||||
|
||||
// -----------------------------------
|
||||
|
|
@ -22,34 +25,16 @@ public class AccountController : ControllerBase
|
|||
{
|
||||
try
|
||||
{
|
||||
var model = new Accounts
|
||||
var createDto = new CreateAccountDTO
|
||||
{
|
||||
Name = dto.Name,
|
||||
Type = dto.Type,
|
||||
Importance = dto.Importance,
|
||||
TerritoryId = dto.TerritoryId,
|
||||
OwnerEmployeeId = dto.OwnerEmployeeId,
|
||||
PrivacySetting = dto.PrivacySetting,
|
||||
About = dto.About,
|
||||
Tags = dto.Tags,
|
||||
|
||||
Name = dto.Name ?? throw new ArgumentException("Name is required"),
|
||||
Phone = dto.Phone,
|
||||
Email = dto.Email,
|
||||
IndustryId = dto.IndustryId,
|
||||
TimeZone = dto.TimeZone,
|
||||
Website = dto.Website,
|
||||
CurrencyCode = dto.CurrencyCode,
|
||||
ServiceInstructions = dto.ServiceInstructions,
|
||||
|
||||
FacebookUrl = dto.FacebookUrl,
|
||||
LinkedInUrl = dto.LinkedInUrl,
|
||||
TwitterUrl = dto.TwitterUrl,
|
||||
|
||||
CreatedDate = DateTime.UtcNow
|
||||
Website = dto.Website
|
||||
};
|
||||
|
||||
_db.Accounts.Add(model);
|
||||
await _db.SaveChangesAsync();
|
||||
var userId = User.Identity?.Name ?? "system";
|
||||
await _accountService.CreateAccountAsync(createDto, userId);
|
||||
|
||||
return Ok(new DataResponse
|
||||
{
|
||||
|
|
@ -75,34 +60,16 @@ public class AccountController : ControllerBase
|
|||
{
|
||||
try
|
||||
{
|
||||
var account = await _db.Accounts.FirstOrDefaultAsync(a => a.Id == dto.Id);
|
||||
if (account == null)
|
||||
return BadRequest(new Response { Status = "Error", Message = "Account not found." });
|
||||
var updateDto = new UpdateAccountDTO
|
||||
{
|
||||
Name = dto.Name,
|
||||
Phone = dto.Phone,
|
||||
Email = dto.Email,
|
||||
Website = dto.Website
|
||||
};
|
||||
|
||||
account.Name = dto.Name;
|
||||
account.Type = dto.Type;
|
||||
account.Importance = dto.Importance;
|
||||
account.TerritoryId = dto.TerritoryId;
|
||||
account.OwnerEmployeeId = dto.OwnerEmployeeId;
|
||||
account.PrivacySetting = dto.PrivacySetting;
|
||||
account.About = dto.About;
|
||||
account.Tags = dto.Tags;
|
||||
|
||||
account.Phone = dto.Phone;
|
||||
account.Email = dto.Email;
|
||||
account.IndustryId = dto.IndustryId;
|
||||
account.TimeZone = dto.TimeZone;
|
||||
account.Website = dto.Website;
|
||||
account.CurrencyCode = dto.CurrencyCode;
|
||||
account.ServiceInstructions = dto.ServiceInstructions;
|
||||
|
||||
account.FacebookUrl = dto.FacebookUrl;
|
||||
account.LinkedInUrl = dto.LinkedInUrl;
|
||||
account.TwitterUrl = dto.TwitterUrl;
|
||||
|
||||
account.LastModificationTime = DateTime.UtcNow;
|
||||
|
||||
await _db.SaveChangesAsync();
|
||||
var userId = User.Identity?.Name ?? "system";
|
||||
await _accountService.UpdateAccountAsync(dto.Id, updateDto, userId);
|
||||
|
||||
return Ok(new DataResponse
|
||||
{
|
||||
|
|
@ -128,12 +95,8 @@ public class AccountController : ControllerBase
|
|||
{
|
||||
try
|
||||
{
|
||||
var account = await _db.Accounts.FirstOrDefaultAsync(a => a.Id == id);
|
||||
if (account == null)
|
||||
return BadRequest(new Response { Status = "Error", Message = "Account not found." });
|
||||
|
||||
_db.Accounts.Remove(account);
|
||||
await _db.SaveChangesAsync();
|
||||
var userId = User.Identity?.Name ?? "system";
|
||||
await _accountService.DeleteAccountAsync(id, userId);
|
||||
|
||||
return Ok(new DataResponse
|
||||
{
|
||||
|
|
@ -155,49 +118,28 @@ public class AccountController : ControllerBase
|
|||
// GET: api/Account/GetAccountList?search=&page=&pageSize=
|
||||
// -----------------------------------
|
||||
[HttpGet("GetAccountList")]
|
||||
public IActionResult GetAccountList(string? search = "", int page = 1, int pageSize = 12)
|
||||
public async Task<IActionResult> GetAccountList(string? search = "", int page = 1, int pageSize = 12)
|
||||
{
|
||||
search ??= "";
|
||||
var pagedResult = await _accountService.GetAccountsPagedAsync(page, pageSize, search);
|
||||
|
||||
var query = _db.Accounts
|
||||
.Where(a => a.IsDeleted == false || a.IsDeleted == null);
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(search))
|
||||
var data = pagedResult.Items.Select(a => new
|
||||
{
|
||||
var s = search.ToLower();
|
||||
query = query.Where(a =>
|
||||
(a.Name ?? "").ToLower().Contains(s) ||
|
||||
(a.Email ?? "").ToLower().Contains(s) ||
|
||||
(a.Phone ?? "").Contains(s) ||
|
||||
(a.Website ?? "").ToLower().Contains(s)
|
||||
);
|
||||
}
|
||||
|
||||
var totalCount = query.Count();
|
||||
|
||||
var data = query
|
||||
.OrderByDescending(a => a.Id)
|
||||
.Skip((page - 1) * pageSize)
|
||||
.Take(pageSize)
|
||||
.Select(a => new
|
||||
{
|
||||
a.Id,
|
||||
a.Name,
|
||||
a.Type,
|
||||
a.Phone,
|
||||
a.Email,
|
||||
Industry = a.IndustryId,
|
||||
a.Website
|
||||
})
|
||||
.ToList();
|
||||
Id = a.Id,
|
||||
Name = a.Name,
|
||||
Type = a.Type ?? "",
|
||||
Phone = a.Phone,
|
||||
Email = a.Email,
|
||||
Industry = (int?)null, // Map if needed
|
||||
Website = a.Website
|
||||
});
|
||||
|
||||
var viewModel = new Pagination_DTO
|
||||
{
|
||||
Data = data,
|
||||
PageNumber = page,
|
||||
PageSize = pageSize,
|
||||
TotalCount = totalCount,
|
||||
TotalPages = (int)Math.Ceiling(totalCount / (double)pageSize)
|
||||
TotalCount = pagedResult.TotalCount,
|
||||
TotalPages = (int)Math.Ceiling(pagedResult.TotalCount / (double)pageSize)
|
||||
};
|
||||
|
||||
return Ok(viewModel);
|
||||
|
|
|
|||
|
|
@ -4,6 +4,8 @@ using Microsoft.AspNetCore.Authorization;
|
|||
using Microsoft.AspNetCore.Identity;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using SeaHaven.Services.Interfaces;
|
||||
using SeaHaven.Services.DTOs;
|
||||
|
||||
namespace Api.SeaHavenIndustries.Controllers
|
||||
{
|
||||
|
|
@ -13,12 +15,12 @@ namespace Api.SeaHavenIndustries.Controllers
|
|||
public class AssetController : Controller
|
||||
{
|
||||
private readonly UserManager<ApplicationUser> _userManager;
|
||||
private readonly ApplicationDbContext _db;
|
||||
|
||||
public AssetController(UserManager<ApplicationUser> userManager, ApplicationDbContext db)
|
||||
private readonly IAssetService _assetService;
|
||||
|
||||
public AssetController(UserManager<ApplicationUser> userManager, IAssetService assetService)
|
||||
{
|
||||
_userManager = userManager;
|
||||
_db = db;
|
||||
_assetService = assetService;
|
||||
}
|
||||
|
||||
[HttpPost]
|
||||
|
|
@ -27,17 +29,15 @@ namespace Api.SeaHavenIndustries.Controllers
|
|||
{
|
||||
try
|
||||
{
|
||||
Assets model = new Assets();
|
||||
model.AssetCode = asset.AssetCode;
|
||||
model.Name = asset.Name;
|
||||
model.Description = asset.Description;
|
||||
model.AccountId = asset.AccountId;
|
||||
model.LocationId = asset.LocationId;
|
||||
model.IsActive = asset.IsActive;
|
||||
|
||||
_db.Assets.Add(model);
|
||||
await _db.SaveChangesAsync();
|
||||
|
||||
var createDto = new CreateAssetDTO
|
||||
{
|
||||
Name = asset.Name ?? throw new ArgumentException("Name is required"),
|
||||
LocationId = asset.LocationId
|
||||
};
|
||||
|
||||
var userId = User.Identity?.Name ?? "system";
|
||||
await _assetService.CreateAssetAsync(createDto, userId);
|
||||
|
||||
return Ok(new DataResponse { Message = "Asset Created Successfully", Status = "200" });
|
||||
}
|
||||
catch (Exception ex)
|
||||
|
|
@ -52,23 +52,15 @@ namespace Api.SeaHavenIndustries.Controllers
|
|||
{
|
||||
try
|
||||
{
|
||||
var exist = _db.Assets.Where(w => w.Id == asset.Id).FirstOrDefault();
|
||||
|
||||
if (exist == null)
|
||||
var updateDto = new UpdateAssetDTO
|
||||
{
|
||||
return BadRequest(new Response { Status = "Error", Message = "Asset not found!" });
|
||||
}
|
||||
Name = asset.Name,
|
||||
LocationId = asset.LocationId
|
||||
};
|
||||
|
||||
var userId = User.Identity?.Name ?? "system";
|
||||
await _assetService.UpdateAssetAsync(asset.Id, updateDto, userId);
|
||||
|
||||
exist.AssetCode = asset.AssetCode;
|
||||
exist.Name = asset.Name;
|
||||
exist.Description = asset.Description;
|
||||
exist.AccountId = asset.AccountId;
|
||||
exist.LocationId = asset.LocationId;
|
||||
exist.IsActive = asset.IsActive;
|
||||
|
||||
_db.Assets.Update(exist);
|
||||
await _db.SaveChangesAsync();
|
||||
|
||||
return Ok(new DataResponse { Message = "Asset Updated Successfully", Status = "200" });
|
||||
}
|
||||
catch (Exception ex)
|
||||
|
|
@ -83,18 +75,8 @@ namespace Api.SeaHavenIndustries.Controllers
|
|||
{
|
||||
try
|
||||
{
|
||||
var exist = _db.Assets.Where(w => w.Id == id).FirstOrDefault();
|
||||
|
||||
if (exist != null)
|
||||
{
|
||||
_db.Remove(exist);
|
||||
}
|
||||
else
|
||||
{
|
||||
return BadRequest(new Response { Status = "Error", Message = "Asset not found!" });
|
||||
}
|
||||
|
||||
await _db.SaveChangesAsync();
|
||||
var userId = User.Identity?.Name ?? "system";
|
||||
await _assetService.DeleteAssetAsync(id, userId);
|
||||
return Ok(new DataResponse { Message = "Asset Deleted Successfully", Status = "200" });
|
||||
}
|
||||
catch (Exception ex)
|
||||
|
|
@ -109,17 +91,13 @@ namespace Api.SeaHavenIndustries.Controllers
|
|||
{
|
||||
try
|
||||
{
|
||||
var asset = await _db.Assets
|
||||
.Include(a => a.Account)
|
||||
.Include(a => a.Location)
|
||||
.Where(w => w.Id == id)
|
||||
.FirstOrDefaultAsync();
|
||||
|
||||
var asset = await _assetService.GetAssetByIdWithDetailsAsync(id);
|
||||
|
||||
if (asset == null)
|
||||
{
|
||||
return NotFound(new Response { Status = "Error", Message = "Asset not found" });
|
||||
}
|
||||
|
||||
|
||||
return Ok(asset);
|
||||
}
|
||||
catch (Exception ex)
|
||||
|
|
@ -130,48 +108,25 @@ namespace Api.SeaHavenIndustries.Controllers
|
|||
|
||||
[HttpGet]
|
||||
[Route("GetAssetList")]
|
||||
public IActionResult GetAssetList(string? search = "", int page = 1, int pageSize = 10)
|
||||
public async Task<IActionResult> GetAssetList(string? search = "", int page = 1, int pageSize = 10)
|
||||
{
|
||||
// Build the query with search filter
|
||||
var query = _db.Assets
|
||||
.Include(a => a.Account)
|
||||
.Include(a => a.Location)
|
||||
.Where(s =>
|
||||
string.IsNullOrEmpty(search) ||
|
||||
(s.Name != null && s.Name.ToLower().Contains(search.ToLower())) ||
|
||||
(s.AssetCode != null && s.AssetCode.ToLower().Contains(search.ToLower())) ||
|
||||
(s.Description != null && s.Description.ToLower().Contains(search.ToLower()))
|
||||
);
|
||||
var pagedResult = await _assetService.GetAssetsPagedAsync(page, pageSize, search);
|
||||
|
||||
// Get total count AFTER applying search filter
|
||||
var totalCount = query.Count();
|
||||
|
||||
// Get paginated data
|
||||
var data = query
|
||||
.Select(n => new
|
||||
{
|
||||
n.Id,
|
||||
n.AssetCode,
|
||||
n.Name,
|
||||
n.Description,
|
||||
n.IsActive,
|
||||
AccountName = n.Account != null ? n.Account.Name : "",
|
||||
LocationName = n.Location != null ? n.Location.Name : ""
|
||||
})
|
||||
.OrderByDescending(d => d.Id)
|
||||
.Skip((page - 1) * pageSize)
|
||||
.Take(pageSize)
|
||||
.ToList();
|
||||
|
||||
var totalPages = (int)Math.Ceiling(totalCount / (double)pageSize);
|
||||
var data = pagedResult.Items.Select(a => new
|
||||
{
|
||||
a.Id,
|
||||
a.Name,
|
||||
LocationName = a.LocationName ?? "",
|
||||
a.CreatedDate
|
||||
});
|
||||
|
||||
var viewModel = new Pagination_DTO
|
||||
{
|
||||
Data = data,
|
||||
PageNumber = page,
|
||||
PageSize = pageSize,
|
||||
TotalCount = totalCount,
|
||||
TotalPages = totalPages
|
||||
TotalCount = pagedResult.TotalCount,
|
||||
TotalPages = (int)Math.Ceiling(pagedResult.TotalCount / (double)pageSize)
|
||||
};
|
||||
|
||||
return Ok(viewModel);
|
||||
|
|
|
|||
|
|
@ -4,13 +4,11 @@ using Microsoft.AspNetCore.Identity;
|
|||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.IdentityModel.Tokens;
|
||||
using System.IdentityModel.Tokens.Jwt;
|
||||
using System.Net.Mail;
|
||||
using System.Net;
|
||||
using System.Security.Claims;
|
||||
using System.Text;
|
||||
using Api.SeaHavenIndustries.DTOs;
|
||||
using Api.SeaHavenIndustries.Helper;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using System.Diagnostics;
|
||||
|
||||
namespace Api.SeaHavenIndustries.Controllers
|
||||
{
|
||||
|
|
@ -21,12 +19,14 @@ namespace Api.SeaHavenIndustries.Controllers
|
|||
private readonly UserManager<ApplicationUser> _userManager;
|
||||
private readonly IConfiguration _configuration;
|
||||
private readonly ApplicationDbContext _db;
|
||||
private readonly SendMessage _sendMessage;
|
||||
|
||||
public AuthenticationController(UserManager<ApplicationUser> userManager, IConfiguration configuration, ApplicationDbContext db, IWebHostEnvironment webHostEnvironment, IHttpContextAccessor httpContext)
|
||||
public AuthenticationController(UserManager<ApplicationUser> userManager, IConfiguration configuration, ApplicationDbContext db, SendMessage sendMessage)
|
||||
{
|
||||
_userManager = userManager;
|
||||
_configuration = configuration;
|
||||
_db = db;
|
||||
_sendMessage = sendMessage;
|
||||
}
|
||||
|
||||
[AllowAnonymous]
|
||||
|
|
@ -34,36 +34,45 @@ namespace Api.SeaHavenIndustries.Controllers
|
|||
[Route("login")]
|
||||
public async Task<IActionResult> Login([FromBody] LoginModel model)
|
||||
{
|
||||
var user = await _userManager.FindByNameAsync(model.Username ?? "");
|
||||
if (user.IsDeleted != true && user != null && await _userManager.CheckPasswordAsync(user, model.Password ?? ""))
|
||||
try
|
||||
{
|
||||
// Standard login without 2FA
|
||||
var userRoles = await _userManager.GetRolesAsync(user);
|
||||
var authClaims = new List<Claim>
|
||||
var user = await _userManager.FindByNameAsync(model.Username ?? "");
|
||||
if (user != null && user.IsDeleted != true && await _userManager.CheckPasswordAsync(user, model.Password ?? ""))
|
||||
{
|
||||
// Standard login without 2FA
|
||||
var userRoles = await _userManager.GetRolesAsync(user);
|
||||
var authClaims = new List<Claim>
|
||||
{
|
||||
new Claim(ClaimTypes.Name, user.UserName ?? ""),
|
||||
new Claim(ClaimTypes.NameIdentifier, user.Id),
|
||||
new Claim(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString())
|
||||
};
|
||||
foreach (var userRole in userRoles)
|
||||
{
|
||||
authClaims.Add(new Claim(ClaimTypes.Role, userRole));
|
||||
foreach (var userRole in userRoles)
|
||||
{
|
||||
authClaims.Add(new Claim(ClaimTypes.Role, userRole));
|
||||
}
|
||||
var token = GetToken(authClaims);
|
||||
return Ok(new
|
||||
{
|
||||
token = new JwtSecurityTokenHandler().WriteToken(token),
|
||||
expiration = token.ValidTo,
|
||||
email = user.Email,
|
||||
userRoles = userRoles.FirstOrDefault(),
|
||||
phoneNumber = user.PhoneNumber,
|
||||
fullname = user.FirstName + " " + user.LastName,
|
||||
id = user.Id
|
||||
});
|
||||
}
|
||||
var token = GetToken(authClaims);
|
||||
return Ok(new
|
||||
{
|
||||
token = new JwtSecurityTokenHandler().WriteToken(token),
|
||||
expiration = token.ValidTo,
|
||||
email = user.Email,
|
||||
userRoles = userRoles.FirstOrDefault(),
|
||||
phoneNumber = user.PhoneNumber,
|
||||
fullname = user.FirstName + " " + user.LastName,
|
||||
id = user.Id
|
||||
});
|
||||
}
|
||||
|
||||
// Invalid credentials
|
||||
return Unauthorized();
|
||||
// Invalid credentials
|
||||
return Unauthorized();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
|
||||
return StatusCode(500, ex.Message);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
[Route("ChangePassword")]
|
||||
|
|
@ -150,7 +159,7 @@ namespace Api.SeaHavenIndustries.Controllers
|
|||
_db.ForgetPasswordCodes.Add(forgetPasswordCode);
|
||||
_db.SaveChanges();
|
||||
string body = $"Your Password Reset Code is: " + forgetPasswordCode.Code;
|
||||
SendEMail(user.Email, "Forget Password Request.", body);
|
||||
await _sendMessage.SendEMail(user.Email, "Forget Password Request.", body);
|
||||
return Ok(new Response { Status = "Success ", Message = "Please check your email for code" });
|
||||
}
|
||||
else
|
||||
|
|
@ -211,42 +220,10 @@ namespace Api.SeaHavenIndustries.Controllers
|
|||
}
|
||||
}
|
||||
|
||||
[HttpGet()]
|
||||
public bool SendEMail(string emailid, string subject, string body)
|
||||
{
|
||||
try
|
||||
{
|
||||
MailMessage mail = new MailMessage();
|
||||
mail.From = new MailAddress("infoesquall@codevoir.com"); //IMPORTANT: This must be same as your smtp authentication address.
|
||||
mail.To.Add(emailid);
|
||||
|
||||
//set the content
|
||||
|
||||
mail.Subject = subject;
|
||||
mail.Body = body;
|
||||
mail.IsBodyHtml = true;
|
||||
//send the message
|
||||
SmtpClient smtp = new SmtpClient("mail.codevoir.com");
|
||||
|
||||
//IMPORANT: Your smtp login email MUST be same as your FROM address.
|
||||
NetworkCredential Credentials = new NetworkCredential("infoesquall@codevoir.com", "Abc123!@#");
|
||||
smtp.UseDefaultCredentials = false;
|
||||
smtp.Credentials = Credentials;
|
||||
smtp.Port = 8889; //25 alternative port number is 8889
|
||||
smtp.EnableSsl = false;
|
||||
|
||||
smtp.Send(mail);
|
||||
return true;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
private Random _random = new Random();
|
||||
private string GenerateRandomNo()
|
||||
{
|
||||
return _random.Next(0, 9999).ToString("D4");
|
||||
return _random.Next(0, 999999).ToString("D6");
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
|
|
|
|||
|
|
@ -30,7 +30,7 @@ namespace Api.SeaHavenIndustries.Controllers
|
|||
try
|
||||
{
|
||||
var allUsersQuery = _db.Users
|
||||
.Where(w => w.FirstName.ToLower().Contains(search.ToLower()))
|
||||
.Where(w => (w.FirstName ?? "").ToLower().Contains((search ?? "").ToLower()))
|
||||
.OrderByDescending(d => d.Id);
|
||||
|
||||
var totalCount = allUsersQuery.Count();
|
||||
|
|
|
|||
|
|
@ -5,20 +5,28 @@ using Microsoft.AspNetCore.Identity;
|
|||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using SendGrid.Helpers.Mail;
|
||||
using SeaHaven.Services.Interfaces;
|
||||
|
||||
namespace Api.SeaHavenIndustries.Controllers
|
||||
{
|
||||
//[Authorize] // Removed for testing
|
||||
[ApiController]
|
||||
[Route("api/Contact")]
|
||||
[Route("api/contacts")]
|
||||
public class ContactController : Controller
|
||||
{
|
||||
private readonly UserManager<ApplicationUser> _userManager;
|
||||
private readonly ApplicationDbContext _db;
|
||||
public ContactController(UserManager<ApplicationUser> userManager, ApplicationDbContext db)
|
||||
private readonly IContactService _contactService;
|
||||
private readonly ILocationService _locationService;
|
||||
|
||||
public ContactController(
|
||||
UserManager<ApplicationUser> userManager,
|
||||
IContactService contactService,
|
||||
ILocationService locationService)
|
||||
{
|
||||
_userManager = userManager;
|
||||
_db = db;
|
||||
_contactService = contactService;
|
||||
_locationService = locationService;
|
||||
}
|
||||
[HttpPost]
|
||||
[Route("AddContacts")]
|
||||
|
|
@ -26,27 +34,10 @@ namespace Api.SeaHavenIndustries.Controllers
|
|||
{
|
||||
try
|
||||
{
|
||||
Contacts model = new Contacts();
|
||||
model.Title = contacts.Title;
|
||||
model.Owner = contacts.Owner;
|
||||
model.FirstName = contacts.FirstName;
|
||||
model.MiddleName = contacts.MiddleName;
|
||||
model.LastName = contacts.LastName;
|
||||
model.ContactType = contacts.ContactType;
|
||||
model.PhoneNumber = contacts.PhoneNumber;
|
||||
model.Email = contacts.Email;
|
||||
model.Address1 = contacts.Address1;
|
||||
model.Address2 = contacts.Address2;
|
||||
model.City = contacts.City;
|
||||
model.State = contacts.State;
|
||||
model.Zip = contacts.Zip;
|
||||
model.FacebookUrl = contacts.FacebookUrl;
|
||||
model.LinkedInUrl = contacts.LinkedInUrl;
|
||||
model.TwitterUrl = contacts.TwitterUrl;
|
||||
model.LocationId = contacts.LocationId;
|
||||
model.AccountId = contacts.AccountId;
|
||||
_db.Contacts.Add(model);
|
||||
await _db.SaveChangesAsync();
|
||||
// ✅ Use DTO mapping method instead of manual property assignment
|
||||
var userId = _userManager.GetUserId(User) ?? "system";
|
||||
await _contactService.CreateContactAsync(contacts.ToServiceCreateDTO(), userId);
|
||||
|
||||
return Ok(new DataResponse { Message = "Updated Successfully", Status = "200" });
|
||||
}
|
||||
catch (Exception ex)
|
||||
|
|
@ -61,53 +52,39 @@ namespace Api.SeaHavenIndustries.Controllers
|
|||
{
|
||||
try
|
||||
{
|
||||
var exist = _db.Contacts.Where(w => w.Id == contacts.Id).FirstOrDefault();
|
||||
// ✅ Use DTO mapping method instead of manual property assignment
|
||||
var userId = _userManager.GetUserId(User) ?? "system";
|
||||
await _contactService.UpdateContactAsync(contacts.Id, contacts.ToServiceUpdateDTO(), userId);
|
||||
|
||||
exist.Title = contacts.Title;
|
||||
exist.Owner = contacts.Owner;
|
||||
exist.FirstName = contacts.FirstName;
|
||||
exist.MiddleName = contacts.MiddleName;
|
||||
exist.LastName = contacts.LastName;
|
||||
exist.ContactType = contacts.ContactType;
|
||||
exist.PhoneNumber = contacts.PhoneNumber;
|
||||
exist.Email = contacts.Email;
|
||||
exist.Address1 = contacts.Address1;
|
||||
exist.Address2 = contacts.Address2;
|
||||
exist.City = contacts.City;
|
||||
exist.State = contacts.State;
|
||||
exist.Zip = contacts.Zip;
|
||||
exist.FacebookUrl = contacts.FacebookUrl;
|
||||
exist.LinkedInUrl = contacts.LinkedInUrl;
|
||||
exist.TwitterUrl = contacts.TwitterUrl;
|
||||
exist.LocationId = contacts.LocationId;
|
||||
exist.AccountId = contacts.AccountId;
|
||||
_db.Contacts.Update(exist);
|
||||
await _db.SaveChangesAsync();
|
||||
return Ok(new DataResponse { Message = "Updated Successfully", Status = "200" });
|
||||
}
|
||||
catch (KeyNotFoundException)
|
||||
{
|
||||
return BadRequest(new Response { Status = "Error", Message = "Contact not found" });
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return BadRequest(new Response { Status = "Error", Message = ex.Message });
|
||||
}
|
||||
}
|
||||
|
||||
[HttpGet]
|
||||
[Route("DeleteContacts")]
|
||||
public async Task<IActionResult> DeleteContacts(int id)
|
||||
{
|
||||
try
|
||||
{
|
||||
var exist = _db.Contacts.Where(w => w.Id == id).FirstOrDefault();
|
||||
if (exist != null)
|
||||
{
|
||||
_db.Remove(exist);
|
||||
}
|
||||
else
|
||||
{
|
||||
return BadRequest(new Response { Status = "Error", Message = "ID not matched!" });
|
||||
}
|
||||
await _db.SaveChangesAsync();
|
||||
var userId = _userManager.GetUserId(User) ?? "system";
|
||||
|
||||
// ✅ Call Service layer (not DataService directly)
|
||||
await _contactService.DeleteContactAsync(id, userId);
|
||||
|
||||
return Ok(new DataResponse { Message = "Removed Successfully", Status = "200" });
|
||||
}
|
||||
catch (KeyNotFoundException)
|
||||
{
|
||||
return BadRequest(new Response { Status = "Error", Message = "ID not matched!" });
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return BadRequest(new Response { Status = "Error", Message = ex.Message });
|
||||
|
|
@ -115,32 +92,21 @@ namespace Api.SeaHavenIndustries.Controllers
|
|||
}
|
||||
[HttpGet]
|
||||
[Route("GetContactList")]
|
||||
public IActionResult GetContactList(string? search ="", int page = 1, int pageSize = 10)
|
||||
public async Task<IActionResult> GetContactList(string? search ="", int page = 1, int pageSize = 10)
|
||||
{
|
||||
var totalCount = _db.Contacts.Count();
|
||||
var data = _db.Contacts.Where(s=>
|
||||
(s.FirstName != null && s.FirstName.ToLower().Contains(search.ToLower())) ||
|
||||
(s.LastName != null && s.LastName.ToLower().Contains(search.ToLower())) ||
|
||||
(s.Email != null && s.Email.ToLower().Contains(search.ToLower())) ||
|
||||
(s.PhoneNumber != null && s.PhoneNumber.Contains(search.ToLower()))
|
||||
).Select(n => new
|
||||
{
|
||||
n.Id,
|
||||
Name = (n.FirstName + " " + n.LastName).Trim(),
|
||||
email = n.Email,
|
||||
phone = n.PhoneNumber
|
||||
}).OrderByDescending(d => d.Id).ToList().Skip((page - 1) * pageSize)
|
||||
.Take(pageSize);
|
||||
// Calculate the number of pages
|
||||
var totalPages = (int)Math.Ceiling(totalCount / (double)pageSize);
|
||||
// ✅ 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);
|
||||
|
||||
// Create a view model containing the paged items and pagination metadata
|
||||
var viewModel = new Pagination_DTO
|
||||
{
|
||||
Data = data,
|
||||
PageNumber = page,
|
||||
PageSize = pageSize,
|
||||
TotalCount = totalCount,
|
||||
TotalCount = pagedResult.TotalCount,
|
||||
TotalPages = totalPages
|
||||
};
|
||||
return Ok(viewModel);
|
||||
|
|
@ -150,52 +116,54 @@ namespace Api.SeaHavenIndustries.Controllers
|
|||
[Route("GetContacts")]
|
||||
public async Task<IActionResult> GetContacts()
|
||||
{
|
||||
var data = await _db.Contacts.ToListAsync();
|
||||
// ✅ Call Service layer
|
||||
var data = await _contactService.GetAllContactsAsync();
|
||||
return Ok(data);
|
||||
}
|
||||
[HttpGet]
|
||||
[Route("GetLocations")]
|
||||
public async Task<IActionResult> GetLocations()
|
||||
{
|
||||
var data = await _db.Locations.Select(s => new LocationViewModel
|
||||
{
|
||||
Id = s.Id,
|
||||
Name = s.Title + " - " + s.Name
|
||||
}).ToListAsync();
|
||||
// ✅ 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()
|
||||
{
|
||||
var data = await _db.Categories.ToListAsync();
|
||||
return Ok(data);
|
||||
// ❌ TEMPORARY: Needs ICategoryService
|
||||
return StatusCode(501, new Response { Status = "Error", Message = "Not Implemented - Needs CategoryService" });
|
||||
}
|
||||
|
||||
[HttpGet]
|
||||
[Route("GetUsers")]
|
||||
public async Task<IActionResult> GetUsers()
|
||||
{
|
||||
var filteredUsers = await (from user in _db.Users
|
||||
where !(from userRole in _db.UserRoles
|
||||
join role in _db.Roles on userRole.RoleId equals role.Id
|
||||
where role.Name == "Admin" || role.Name == "Manager"
|
||||
select userRole.UserId).Contains(user.Id)
|
||||
select user).ToListAsync();
|
||||
return Ok(filteredUsers);
|
||||
// ❌ 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")]
|
||||
public async Task<IActionResult> AddLocation(Location_DTO model)
|
||||
{
|
||||
try
|
||||
{
|
||||
Locations locations = new Locations();
|
||||
locations.Title = model.Title;
|
||||
locations.Name = model.Name;
|
||||
_db.Locations.Add(locations);
|
||||
await _db.SaveChangesAsync();
|
||||
// ✅ 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" });
|
||||
}
|
||||
catch (Exception ex)
|
||||
|
|
@ -203,26 +171,22 @@ namespace Api.SeaHavenIndustries.Controllers
|
|||
return BadRequest(new Response { Status = "Error", Message = ex.Message });
|
||||
}
|
||||
}
|
||||
|
||||
[HttpPost]
|
||||
[Route("EditLocation")]
|
||||
public async Task<IActionResult> EditLocation(EditLocation_DTO model)
|
||||
{
|
||||
try
|
||||
{
|
||||
var exist = _db.Locations.Where(w => w.Id == model.Id).FirstOrDefault();
|
||||
if (exist != null)
|
||||
{
|
||||
exist.Title = model.Title;
|
||||
exist.Name = model.Name;
|
||||
}
|
||||
else
|
||||
{
|
||||
return BadRequest(new Response { Status = "Error", Message = "ID not matched!" });
|
||||
}
|
||||
_db.Locations.Update(exist);
|
||||
await _db.SaveChangesAsync();
|
||||
// ✅ 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" });
|
||||
}
|
||||
catch (KeyNotFoundException)
|
||||
{
|
||||
return BadRequest(new Response { Status = "Error", Message = "ID not matched!" });
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return BadRequest(new Response { Status = "Error", Message = ex.Message });
|
||||
|
|
@ -235,20 +199,15 @@ namespace Api.SeaHavenIndustries.Controllers
|
|||
{
|
||||
try
|
||||
{
|
||||
var data1 = await _db.Templates.ToListAsync();
|
||||
_db.Templates.RemoveRange(data1);
|
||||
var data = await _db.Locations.Where(w => w.Id == Id).FirstOrDefaultAsync();
|
||||
if (data != null)
|
||||
{
|
||||
_db.Locations.Remove(data ?? new Locations());
|
||||
}
|
||||
else
|
||||
{
|
||||
return BadRequest(new Response { Status = "Error", Message = "ID not matched!" });
|
||||
}
|
||||
await _db.SaveChangesAsync();
|
||||
// ✅ Use Service layer
|
||||
var userId = _userManager.GetUserId(User) ?? "system";
|
||||
await _locationService.DeleteLocationAsync(Id, userId);
|
||||
return Ok(new DataResponse { Message = "Updated Successfully", Status = "200" });
|
||||
}
|
||||
catch (KeyNotFoundException)
|
||||
{
|
||||
return BadRequest(new Response { Status = "Error", Message = "ID not matched!" });
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return BadRequest(new Response { Status = "Error", Message = ex.Message });
|
||||
|
|
@ -257,36 +216,25 @@ namespace Api.SeaHavenIndustries.Controllers
|
|||
|
||||
[HttpGet]
|
||||
[Route("GetAddressbook")]
|
||||
public IActionResult GetAddressbook(string? search = "", int page = 1, int pageSize = 10)
|
||||
public async Task<IActionResult> GetAddressbook(string? search = "", int page = 1, int pageSize = 10)
|
||||
{
|
||||
var totalCount = _db.Locations.Count();
|
||||
var data = _db.Locations.Where(w=> w.Name.ToLower().Contains(search.ToLower()) || w.Title.ToLower().Contains(search.ToLower())).Select(n => new
|
||||
{
|
||||
n.Id,
|
||||
n.Name,
|
||||
n.Title
|
||||
}).OrderByDescending(d => d.Id).ToList().Skip((page - 1) * pageSize)
|
||||
.Take(pageSize);
|
||||
// Calculate the number of pages
|
||||
var totalPages = (int)Math.Ceiling(totalCount / (double)pageSize);
|
||||
// ✅ 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);
|
||||
|
||||
// Create a view model containing the paged items and pagination metadata
|
||||
var viewModel = new Pagination_DTO
|
||||
{
|
||||
Data = data,
|
||||
PageNumber = page,
|
||||
PageSize = pageSize,
|
||||
TotalCount = totalCount,
|
||||
TotalCount = pagedResult.TotalCount,
|
||||
TotalPages = totalPages
|
||||
};
|
||||
return Ok(viewModel);
|
||||
}
|
||||
|
||||
public class LocationViewModel
|
||||
{
|
||||
public int? Id { get; set; }
|
||||
public string? Name { get; set; }
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
|||
39
Api.SeaHavenIndustries/Controllers/DashboardController.cs
Normal file
39
Api.SeaHavenIndustries/Controllers/DashboardController.cs
Normal file
|
|
@ -0,0 +1,39 @@
|
|||
using Data.SeaHavenIndustries;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
|
||||
namespace Api.SeaHavenIndustries.Controllers
|
||||
{
|
||||
[Authorize]
|
||||
[ApiController]
|
||||
[Route("api/[controller]")]
|
||||
public class DashboardController : Controller
|
||||
{
|
||||
private readonly ApplicationDbContext _db;
|
||||
|
||||
public DashboardController(ApplicationDbContext db)
|
||||
{
|
||||
_db = db;
|
||||
}
|
||||
|
||||
[HttpGet("Stats")]
|
||||
public async Task<IActionResult> GetStats()
|
||||
{
|
||||
var query = _db.workOrders.Where(w => w.istemplate != true);
|
||||
|
||||
var total = await query.CountAsync();
|
||||
var open = await query.CountAsync(w => w.Status == "Open" || w.Status == "In Progress" || w.Status == "On Hold");
|
||||
var notDispatched = await query.CountAsync(w => (w.Status == "Open") && (w.AssignTo == null || w.AssignTo == ""));
|
||||
var completed = await query.CountAsync(w => w.Status == "Done");
|
||||
|
||||
return Ok(new
|
||||
{
|
||||
total,
|
||||
open,
|
||||
notDispatched,
|
||||
completed
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
349
Api.SeaHavenIndustries/Controllers/DispatchController.cs
Normal file
349
Api.SeaHavenIndustries/Controllers/DispatchController.cs
Normal file
|
|
@ -0,0 +1,349 @@
|
|||
using Data.SeaHavenIndustries;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using SeaHaven.Services.Interfaces;
|
||||
using SeaHaven.Services.DTOs;
|
||||
|
||||
namespace Api.SeaHavenIndustries.Controllers
|
||||
{
|
||||
[Authorize]
|
||||
[ApiController]
|
||||
[Route("api/[controller]")]
|
||||
[Route("api/dispatches")]
|
||||
public class DispatchController : Controller
|
||||
{
|
||||
private readonly IDispatchService _dispatchService;
|
||||
private readonly ApplicationDbContext _db; // Keep for complex queries
|
||||
|
||||
public DispatchController(IDispatchService dispatchService, ApplicationDbContext db)
|
||||
{
|
||||
_dispatchService = dispatchService;
|
||||
_db = db;
|
||||
}
|
||||
|
||||
[HttpGet]
|
||||
public async Task<IActionResult> List(
|
||||
[FromQuery] string? search = null,
|
||||
[FromQuery] string? status = null,
|
||||
[FromQuery] int? vendorId = null,
|
||||
[FromQuery] DateTime? dateFrom = null,
|
||||
[FromQuery] DateTime? dateTo = null,
|
||||
[FromQuery] string? sortBy = "dispatchedAt",
|
||||
[FromQuery] string? sortDir = "desc",
|
||||
[FromQuery] int page = 1,
|
||||
[FromQuery] int pageSize = 25)
|
||||
{
|
||||
var q = _db.Dispatches
|
||||
.Include(d => d.Vendor)
|
||||
.Include(d => d.WorkOrder).ThenInclude(w => w!.Locations)
|
||||
.Where(d => d.IsDeleted == null || d.IsDeleted == false);
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(search))
|
||||
{
|
||||
var s = search.ToLower();
|
||||
q = q.Where(d =>
|
||||
(d.DispatchNumber ?? "").ToLower().Contains(s) ||
|
||||
(d.PONumber ?? "").ToLower().Contains(s) ||
|
||||
(d.Vendor != null && (d.Vendor.CompanyName ?? "").ToLower().Contains(s)) ||
|
||||
(d.WorkOrder != null && (d.WorkOrder.InternalWONumber ?? "").ToLower().Contains(s)) ||
|
||||
(d.WorkOrder != null && (d.WorkOrder.WorkerOrderTitle ?? "").ToLower().Contains(s)) ||
|
||||
(d.WorkOrder != null && d.WorkOrder.Locations != null && (d.WorkOrder.Locations.Name ?? "").ToLower().Contains(s))
|
||||
);
|
||||
}
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(status))
|
||||
q = q.Where(d => d.Status == status);
|
||||
|
||||
if (vendorId.HasValue)
|
||||
q = q.Where(d => d.VendorId == vendorId.Value);
|
||||
|
||||
if (dateFrom.HasValue)
|
||||
q = q.Where(d => d.DispatchedAt >= dateFrom.Value);
|
||||
|
||||
if (dateTo.HasValue)
|
||||
{
|
||||
var end = dateTo.Value.Date.AddDays(1);
|
||||
q = q.Where(d => d.DispatchedAt < end);
|
||||
}
|
||||
|
||||
var total = await q.CountAsync();
|
||||
|
||||
var dir = (sortDir ?? "desc").ToLowerInvariant() == "asc";
|
||||
switch ((sortBy ?? "").ToLowerInvariant())
|
||||
{
|
||||
case "ponumber":
|
||||
q = dir ? q.OrderBy(d => d.PONumber) : q.OrderByDescending(d => d.PONumber);
|
||||
break;
|
||||
case "dispatchnumber":
|
||||
q = dir ? q.OrderBy(d => d.DispatchNumber) : q.OrderByDescending(d => d.DispatchNumber);
|
||||
break;
|
||||
case "vendor":
|
||||
q = dir ? q.OrderBy(d => d.Vendor!.CompanyName) : q.OrderByDescending(d => d.Vendor!.CompanyName);
|
||||
break;
|
||||
case "nte":
|
||||
case "nteamount":
|
||||
q = dir ? q.OrderBy(d => d.NTEAmount) : q.OrderByDescending(d => d.NTEAmount);
|
||||
break;
|
||||
case "status":
|
||||
q = dir ? q.OrderBy(d => d.Status) : q.OrderByDescending(d => d.Status);
|
||||
break;
|
||||
case "scheduleddate":
|
||||
q = dir ? q.OrderBy(d => d.ScheduledDate) : q.OrderByDescending(d => d.ScheduledDate);
|
||||
break;
|
||||
case "completeddate":
|
||||
q = dir ? q.OrderBy(d => d.CompletedDate) : q.OrderByDescending(d => d.CompletedDate);
|
||||
break;
|
||||
default:
|
||||
q = dir ? q.OrderBy(d => d.DispatchedAt) : q.OrderByDescending(d => d.DispatchedAt);
|
||||
break;
|
||||
}
|
||||
|
||||
var rows = await q
|
||||
.Skip((Math.Max(page, 1) - 1) * pageSize)
|
||||
.Take(pageSize)
|
||||
.Select(d => new
|
||||
{
|
||||
d.Id,
|
||||
d.DispatchNumber,
|
||||
d.PONumber,
|
||||
d.NTEAmount,
|
||||
d.Status,
|
||||
d.DispatchedAt,
|
||||
d.ScheduledDate,
|
||||
d.CompletedDate,
|
||||
d.AcknowledgedAt,
|
||||
d.VerifiedAt,
|
||||
VendorId = d.VendorId,
|
||||
VendorName = d.Vendor != null ? d.Vendor.CompanyName : null,
|
||||
WorkOrderId = d.WorkOrderId,
|
||||
PrimaryWOInternalNumber = d.WorkOrder != null ? d.WorkOrder.InternalWONumber : null,
|
||||
PrimaryWOTitle = d.WorkOrder != null ? d.WorkOrder.WorkerOrderTitle : null,
|
||||
LocationName = d.WorkOrder != null && d.WorkOrder.Locations != null ? d.WorkOrder.Locations.Name : null,
|
||||
WorkOrderCount = _db.DispatchWorkOrders.Count(dwo => dwo.DispatchId == d.Id),
|
||||
PendingUpliftCount = _db.DispatchUpliftRequests.Count(u => u.DispatchId == d.Id && u.Status == "Pending" && (u.IsDeleted == null || u.IsDeleted == false))
|
||||
})
|
||||
.ToListAsync();
|
||||
|
||||
return Ok(new DataResponse
|
||||
{
|
||||
Status = "Success",
|
||||
Data = new { total, page, pageSize, items = rows }
|
||||
});
|
||||
}
|
||||
|
||||
[HttpGet("{id:int}")]
|
||||
public async Task<IActionResult> GetById(int id)
|
||||
{
|
||||
var dispatch = await _db.Dispatches
|
||||
.Include(d => d.Vendor)
|
||||
.Include(d => d.WorkOrder).ThenInclude(w => w!.Locations)
|
||||
.FirstOrDefaultAsync(d => d.Id == id);
|
||||
if (dispatch == null) return NotFound(new Response { Status = "Error", Message = "Dispatch not found" });
|
||||
|
||||
var linkedWOs = await _db.DispatchWorkOrders
|
||||
.Where(dwo => dwo.DispatchId == id)
|
||||
.Select(dwo => new
|
||||
{
|
||||
dwo.WorkOrderId,
|
||||
InternalWONumber = dwo.WorkOrder != null ? dwo.WorkOrder.InternalWONumber : null,
|
||||
Title = dwo.WorkOrder != null ? dwo.WorkOrder.WorkerOrderTitle : null,
|
||||
Priority = dwo.WorkOrder != null ? dwo.WorkOrder.Priority : null,
|
||||
Status = dwo.WorkOrder != null ? dwo.WorkOrder.Status : null,
|
||||
LocationName = dwo.WorkOrder != null && dwo.WorkOrder.Locations != null ? dwo.WorkOrder.Locations.Name : null,
|
||||
Trade = dwo.WorkOrder != null ? dwo.WorkOrder.Trade : null,
|
||||
SubTrade = dwo.WorkOrder != null ? dwo.WorkOrder.SubTrade : null,
|
||||
Problem = dwo.WorkOrder != null ? dwo.WorkOrder.Problem : null
|
||||
}).ToListAsync();
|
||||
|
||||
if (linkedWOs.Count == 0 && dispatch.WorkOrderId.HasValue)
|
||||
{
|
||||
var wo = await _db.workOrders
|
||||
.Include(w => w.Locations)
|
||||
.FirstOrDefaultAsync(w => w.Id == dispatch.WorkOrderId.Value);
|
||||
if (wo != null)
|
||||
{
|
||||
linkedWOs = new[] { new
|
||||
{
|
||||
WorkOrderId = wo.Id,
|
||||
InternalWONumber = wo.InternalWONumber,
|
||||
Title = wo.WorkerOrderTitle,
|
||||
Priority = wo.Priority,
|
||||
Status = wo.Status,
|
||||
LocationName = wo.Locations != null ? wo.Locations.Name : null,
|
||||
Trade = wo.Trade,
|
||||
SubTrade = wo.SubTrade,
|
||||
Problem = wo.Problem
|
||||
}}.ToList();
|
||||
}
|
||||
}
|
||||
|
||||
var checklist = await _db.DispatchChecklistItems
|
||||
.Where(c => c.DispatchId == id && (c.IsDeleted == null || c.IsDeleted == false))
|
||||
.OrderBy(c => c.SortOrder)
|
||||
.Select(c => new { c.Id, c.ItemText, c.IsCompleted, c.CompletedBy, c.CompletedAt, c.SortOrder, c.WorkOrderId })
|
||||
.ToListAsync();
|
||||
|
||||
var signoffs = await _db.DispatchSignoffs
|
||||
.Where(s => s.DispatchId == id && (s.IsDeleted == null || s.IsDeleted == false))
|
||||
.Select(s => new { s.Id, s.SignoffType, s.Name, s.Signature, s.SignatureMethod, s.SignedAt })
|
||||
.ToListAsync();
|
||||
|
||||
var rawComments = await (from c in _db.Comments
|
||||
where c.DispatchId == id && (c.IsDeleted == null || c.IsDeleted == false)
|
||||
join u in _db.Users on c.UserId equals u.Id into users
|
||||
from u in users.DefaultIfEmpty()
|
||||
orderby c.CreatedDate
|
||||
select new
|
||||
{
|
||||
c.Id,
|
||||
c.Commenttext,
|
||||
c.Commenter,
|
||||
c.CommentType,
|
||||
c.RecordType,
|
||||
c.CreatedDate,
|
||||
c.UserId,
|
||||
UserFirstName = u != null ? u.FirstName : null,
|
||||
UserLastName = u != null ? u.LastName : null
|
||||
})
|
||||
.ToListAsync();
|
||||
|
||||
var comments = rawComments.Select(c => new
|
||||
{
|
||||
c.Id,
|
||||
c.Commenttext,
|
||||
c.Commenter,
|
||||
c.CommentType,
|
||||
c.RecordType,
|
||||
c.CreatedDate,
|
||||
UserName = string.Join(" ", new[] { c.UserFirstName, c.UserLastName }
|
||||
.Where(s => !string.IsNullOrWhiteSpace(s))).Trim()
|
||||
}).ToList();
|
||||
|
||||
var rawUplifts = await (from u in _db.DispatchUpliftRequests
|
||||
where u.DispatchId == id && (u.IsDeleted == null || u.IsDeleted == false)
|
||||
join dec in _db.Users on u.DecidedByUserId equals dec.Id into decs
|
||||
from dec in decs.DefaultIfEmpty()
|
||||
orderby u.CreatedDate descending
|
||||
select new
|
||||
{
|
||||
u.Id,
|
||||
u.CurrentNTE,
|
||||
u.RequestedNTE,
|
||||
u.VendorReason,
|
||||
u.Status,
|
||||
u.RequiredTier,
|
||||
u.RequestedByVendorName,
|
||||
RequestedAt = u.CreatedDate,
|
||||
u.DecidedAt,
|
||||
u.DecisionNote,
|
||||
DecidedByFirstName = dec != null ? dec.FirstName : null,
|
||||
DecidedByLastName = dec != null ? dec.LastName : null
|
||||
})
|
||||
.ToListAsync();
|
||||
|
||||
var upliftRequests = rawUplifts.Select(u => new
|
||||
{
|
||||
u.Id,
|
||||
u.CurrentNTE,
|
||||
u.RequestedNTE,
|
||||
Delta = u.RequestedNTE - (u.CurrentNTE ?? 0m),
|
||||
u.VendorReason,
|
||||
u.Status,
|
||||
u.RequiredTier,
|
||||
u.RequestedByVendorName,
|
||||
u.RequestedAt,
|
||||
u.DecidedAt,
|
||||
u.DecisionNote,
|
||||
DecidedByName = string.Join(" ", new[] { u.DecidedByFirstName, u.DecidedByLastName }
|
||||
.Where(s => !string.IsNullOrWhiteSpace(s))).Trim()
|
||||
}).ToList();
|
||||
|
||||
var dispatchNumber = dispatch.DispatchNumber ?? "";
|
||||
var workOrderIds = linkedWOs.Select(l => l.WorkOrderId).Where(wid => wid > 0).ToList();
|
||||
|
||||
var rawAudit = await (from a in _db.WorkOrderAuditLogs
|
||||
where workOrderIds.Contains(a.WorkOrderId)
|
||||
&& a.FieldName != null
|
||||
&& a.FieldName.Contains(dispatchNumber)
|
||||
join u in _db.Users on a.UserId equals u.Id into users
|
||||
from u in users.DefaultIfEmpty()
|
||||
orderby a.CreatedAt descending
|
||||
select new
|
||||
{
|
||||
a.Id,
|
||||
a.Action,
|
||||
a.FieldName,
|
||||
a.OldValue,
|
||||
a.NewValue,
|
||||
a.CreatedAt,
|
||||
a.UserId,
|
||||
UserFirstName = u != null ? u.FirstName : null,
|
||||
UserLastName = u != null ? u.LastName : null
|
||||
})
|
||||
.ToListAsync();
|
||||
|
||||
var auditLog = rawAudit.Select(a => new
|
||||
{
|
||||
Id = a.Id,
|
||||
Action = a.Action,
|
||||
FieldName = a.FieldName,
|
||||
OldValue = a.OldValue,
|
||||
NewValue = a.NewValue,
|
||||
CreatedAt = a.CreatedAt,
|
||||
UserName = string.Join(" ", new[] { a.UserFirstName, a.UserLastName }
|
||||
.Where(s => !string.IsNullOrWhiteSpace(s))).Trim()
|
||||
}).ToList();
|
||||
|
||||
return Ok(new DataResponse
|
||||
{
|
||||
Status = "Success",
|
||||
Data = new
|
||||
{
|
||||
dispatch.Id,
|
||||
dispatch.DispatchNumber,
|
||||
dispatch.PONumber,
|
||||
dispatch.NTEAmount,
|
||||
dispatch.Description,
|
||||
dispatch.Status,
|
||||
dispatch.ScheduledDate,
|
||||
dispatch.CompletedDate,
|
||||
dispatch.DispatchedAt,
|
||||
dispatch.AcknowledgedAt,
|
||||
dispatch.VerifiedAt,
|
||||
dispatch.VerifiedBy,
|
||||
dispatch.EmailSent,
|
||||
dispatch.WorkOrderId,
|
||||
Vendor = dispatch.Vendor == null ? null : new
|
||||
{
|
||||
dispatch.Vendor.Id,
|
||||
dispatch.Vendor.CompanyName,
|
||||
dispatch.Vendor.ContactName,
|
||||
dispatch.Vendor.Email,
|
||||
dispatch.Vendor.Phone,
|
||||
dispatch.Vendor.Address,
|
||||
dispatch.Vendor.City,
|
||||
dispatch.Vendor.State,
|
||||
dispatch.Vendor.Zip,
|
||||
dispatch.Vendor.TradeSpecialties
|
||||
},
|
||||
PrimaryLocation = dispatch.WorkOrder?.Locations == null ? null : new
|
||||
{
|
||||
dispatch.WorkOrder.Locations.Id,
|
||||
dispatch.WorkOrder.Locations.Name,
|
||||
dispatch.WorkOrder.Locations.Address1,
|
||||
dispatch.WorkOrder.Locations.City,
|
||||
dispatch.WorkOrder.Locations.State,
|
||||
dispatch.WorkOrder.Locations.Zip
|
||||
},
|
||||
WorkOrders = linkedWOs,
|
||||
Checklist = checklist,
|
||||
Signoffs = signoffs,
|
||||
Comments = comments,
|
||||
UpliftRequests = upliftRequests,
|
||||
AuditLog = auditLog
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,43 @@
|
|||
using Microsoft.AspNetCore.Mvc;
|
||||
|
||||
namespace Api.SeaHavenIndustries.Controllers
|
||||
{
|
||||
[ApiController]
|
||||
[Route("api/dispatch")]
|
||||
public class DispatchPublicController : Controller
|
||||
{
|
||||
[HttpGet("accept/{token}")]
|
||||
public IActionResult Accept(string token)
|
||||
{
|
||||
return Content(BuildHtml(), "text/html");
|
||||
}
|
||||
|
||||
private static string BuildHtml()
|
||||
{
|
||||
return @"<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset='utf-8'/>
|
||||
<meta name='viewport' content='width=device-width, initial-scale=1'/>
|
||||
<title>Link Expired - Sea Haven Industries</title>
|
||||
<style>
|
||||
body { font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; display: flex; justify-content: center; align-items: center; min-height: 100vh; margin: 0; background: #f5f5f5; }
|
||||
.card { background: white; border-radius: 12px; padding: 40px; max-width: 480px; text-align: center; box-shadow: 0 4px 20px rgba(0,0,0,0.1); }
|
||||
.icon { width: 60px; height: 60px; border-radius: 50%; background: #ffc107; color: white; font-size: 28px; display: flex; align-items: center; justify-content: center; margin: 0 auto 20px; }
|
||||
h1 { font-size: 22px; color: #333; margin: 0 0 12px; }
|
||||
p { font-size: 15px; color: #666; line-height: 1.6; margin: 0; }
|
||||
.footer { margin-top: 30px; font-size: 12px; color: #999; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class='card'>
|
||||
<div class='icon'>!</div>
|
||||
<h1>Link No Longer Active</h1>
|
||||
<p>This dispatch accept link has been retired. Please use the vendor portal link in your most recent dispatch email, or contact your dispatcher for a new link.</p>
|
||||
<div class='footer'>Sea Haven Industries</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,87 @@
|
|||
using Data.SeaHavenIndustries;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
|
||||
namespace Api.SeaHavenIndustries.Controllers
|
||||
{
|
||||
[Authorize]
|
||||
[ApiController]
|
||||
[Route("api/[controller]")]
|
||||
public class DropdownOptionsController : Controller
|
||||
{
|
||||
private readonly ApplicationDbContext _db;
|
||||
|
||||
public DropdownOptionsController(ApplicationDbContext db)
|
||||
{
|
||||
_db = db;
|
||||
}
|
||||
|
||||
[HttpGet]
|
||||
public async Task<IActionResult> GetAll([FromQuery] string? category = null)
|
||||
{
|
||||
var query = _db.DropdownOptions.Where(d => d.IsActive);
|
||||
if (!string.IsNullOrWhiteSpace(category))
|
||||
query = query.Where(d => d.Category == category);
|
||||
|
||||
var data = await query.OrderBy(d => d.Category).ThenBy(d => d.SortOrder).ThenBy(d => d.Value).ToListAsync();
|
||||
return Ok(data);
|
||||
}
|
||||
|
||||
[HttpGet("ByCategory/{category}")]
|
||||
public async Task<IActionResult> GetByCategory(string category, [FromQuery] string? parent = null)
|
||||
{
|
||||
var query = _db.DropdownOptions.Where(d => d.IsActive && d.Category == category);
|
||||
if (!string.IsNullOrWhiteSpace(parent))
|
||||
query = query.Where(d => d.ParentValue == parent);
|
||||
|
||||
var data = await query.OrderBy(d => d.SortOrder).ThenBy(d => d.Value).ToListAsync();
|
||||
return Ok(data);
|
||||
}
|
||||
|
||||
[HttpPost]
|
||||
public async Task<IActionResult> Create([FromBody] DropdownOption model)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(model.Category) || string.IsNullOrWhiteSpace(model.Value))
|
||||
return BadRequest(new { message = "Category and Value are required" });
|
||||
|
||||
var existing = await _db.DropdownOptions
|
||||
.AnyAsync(d => d.Category == model.Category && d.Value == model.Value && d.ParentValue == model.ParentValue);
|
||||
if (existing)
|
||||
return BadRequest(new { message = "This option already exists" });
|
||||
|
||||
model.Id = 0;
|
||||
model.IsActive = true;
|
||||
_db.DropdownOptions.Add(model);
|
||||
await _db.SaveChangesAsync();
|
||||
return Ok(model);
|
||||
}
|
||||
|
||||
[HttpPut("{id}")]
|
||||
public async Task<IActionResult> Update(int id, [FromBody] DropdownOption model)
|
||||
{
|
||||
var item = await _db.DropdownOptions.FindAsync(id);
|
||||
if (item == null)
|
||||
return NotFound(new { message = "Option not found" });
|
||||
|
||||
item.Value = model.Value;
|
||||
item.ParentValue = model.ParentValue;
|
||||
item.SortOrder = model.SortOrder;
|
||||
item.IsActive = model.IsActive;
|
||||
await _db.SaveChangesAsync();
|
||||
return Ok(item);
|
||||
}
|
||||
|
||||
[HttpDelete("{id}")]
|
||||
public async Task<IActionResult> Delete(int id)
|
||||
{
|
||||
var item = await _db.DropdownOptions.FindAsync(id);
|
||||
if (item == null)
|
||||
return NotFound(new { message = "Option not found" });
|
||||
|
||||
_db.DropdownOptions.Remove(item);
|
||||
await _db.SaveChangesAsync();
|
||||
return Ok(new { message = "Deleted" });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -2,16 +2,19 @@
|
|||
using Data.SeaHavenIndustries;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using SeaHaven.Services.Interfaces;
|
||||
using SeaHaven.Services.DTOs;
|
||||
|
||||
[ApiController]
|
||||
[Route("api/Employee")]
|
||||
[Route("api/employees")]
|
||||
public class EmployeeController : Controller
|
||||
{
|
||||
private readonly ApplicationDbContext _db;
|
||||
private readonly IEmployeeService _employeeService;
|
||||
|
||||
public EmployeeController(ApplicationDbContext db)
|
||||
public EmployeeController(IEmployeeService employeeService)
|
||||
{
|
||||
_db = db;
|
||||
_employeeService = employeeService;
|
||||
}
|
||||
|
||||
// -----------------------------------
|
||||
|
|
@ -20,42 +23,27 @@ public class EmployeeController : Controller
|
|||
[HttpPost("AddEmployees")]
|
||||
public async Task<IActionResult> AddEmployees(AddEmployee_DTO dto)
|
||||
{
|
||||
var employee = new Employee
|
||||
try
|
||||
{
|
||||
FirstName = dto.FirstName,
|
||||
LastName = dto.LastName,
|
||||
JobTitleId = dto.JobTitleId,
|
||||
PermissionGroupId = dto.PermissionGroupId,
|
||||
Status = dto.Status,
|
||||
LastLogin = dto.LastLogin,
|
||||
CreatedDate = DateTime.UtcNow
|
||||
};
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(dto.Email))
|
||||
{
|
||||
employee.Emails.Add(new EmployeeEmail
|
||||
var createDto = new CreateEmployeeDTO
|
||||
{
|
||||
Address = dto.Email,
|
||||
IsPrimary = true,
|
||||
EmailTypeId = 1
|
||||
});
|
||||
}
|
||||
FirstName = dto.FirstName,
|
||||
LastName = dto.LastName,
|
||||
JobTitleId = dto.JobTitleId,
|
||||
DepartmentId = null, // Map from PermissionGroupId if needed
|
||||
Email = dto.Email,
|
||||
Phone = dto.Phone
|
||||
};
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(dto.Phone))
|
||||
var userId = User.Identity?.Name ?? "system";
|
||||
await _employeeService.CreateEmployeeAsync(createDto, userId);
|
||||
|
||||
return Ok(new { Message = "Employee added successfully" });
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
employee.Phones.Add(new EmployeePhone
|
||||
{
|
||||
Number = dto.Phone,
|
||||
Extension = dto.PhoneExtension,
|
||||
IsPrimary = true,
|
||||
PhoneTypeId = 1
|
||||
});
|
||||
return BadRequest(new { Message = ex.Message });
|
||||
}
|
||||
|
||||
_db.Employees.Add(employee);
|
||||
await _db.SaveChangesAsync();
|
||||
|
||||
return Ok(new { Message = "Employee added successfully" });
|
||||
}
|
||||
|
||||
// -----------------------------------
|
||||
|
|
@ -64,87 +52,54 @@ public class EmployeeController : Controller
|
|||
[HttpPost("EditEmployees")]
|
||||
public async Task<IActionResult> EditEmployees(EditEmployee_DTO dto)
|
||||
{
|
||||
var employee = await _db.Employees
|
||||
.Include(e => e.Emails)
|
||||
.Include(e => e.Phones)
|
||||
.FirstOrDefaultAsync(e => e.Id == dto.Id);
|
||||
try
|
||||
{
|
||||
var updateDto = new UpdateEmployeeDTO
|
||||
{
|
||||
FirstName = dto.FirstName,
|
||||
LastName = dto.LastName,
|
||||
JobTitleId = dto.JobTitleId
|
||||
};
|
||||
|
||||
if (employee == null)
|
||||
return BadRequest("Employee not found");
|
||||
var userId = User.Identity?.Name ?? "system";
|
||||
await _employeeService.UpdateEmployeeAsync(dto.Id, updateDto, userId);
|
||||
|
||||
employee.FirstName = dto.FirstName;
|
||||
employee.LastName = dto.LastName;
|
||||
employee.JobTitleId = dto.JobTitleId;
|
||||
employee.PermissionGroupId = dto.PermissionGroupId;
|
||||
employee.Status = dto.Status;
|
||||
employee.LastLogin = dto.LastLogin;
|
||||
employee.LastModificationTime = DateTime.UtcNow;
|
||||
|
||||
await _db.SaveChangesAsync();
|
||||
return Ok(new { Message = "Employee updated successfully" });
|
||||
return Ok(new { Message = "Employee updated successfully" });
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return BadRequest(new { Message = ex.Message });
|
||||
}
|
||||
}
|
||||
|
||||
// -----------------------------------
|
||||
// GET: api/Employee/GetEmployeeList
|
||||
// -----------------------------------
|
||||
[HttpGet("GetEmployeeList")]
|
||||
public IActionResult GetEmployeeList(string? search = "", int page = 1, int pageSize = 12)
|
||||
public async Task<IActionResult> GetEmployeeList(string? search = "", int page = 1, int pageSize = 12)
|
||||
{
|
||||
search ??= "";
|
||||
var pagedResult = await _employeeService.GetEmployeesPagedAsync(page, pageSize, search);
|
||||
|
||||
var totalCount = _db.Employees.Where(e => !e.IsDeleted.HasValue || e.IsDeleted == false).Count();
|
||||
var data = pagedResult.Items.Select(e => new
|
||||
{
|
||||
id = e.Id,
|
||||
firstName = e.FirstName,
|
||||
lastName = e.LastName,
|
||||
jobTitle = e.JobTitleName ?? "",
|
||||
email = e.Email ?? "",
|
||||
phone = e.Phone ?? "",
|
||||
department = e.DepartmentName ?? "",
|
||||
status = "Active" // Map from employee status if available
|
||||
});
|
||||
|
||||
var data = _db.Employees
|
||||
.Include(e => e.JobTitle)
|
||||
.Include(e => e.Department)
|
||||
.Include(e => e.Emails)
|
||||
.Include(e => e.Phones)
|
||||
.Where(e => !e.IsDeleted.HasValue || e.IsDeleted == false)
|
||||
.Where(e =>
|
||||
string.IsNullOrWhiteSpace(search) ||
|
||||
(e.FirstName != null && e.FirstName.ToLower().Contains(search.ToLower())) ||
|
||||
(e.LastName != null && e.LastName.ToLower().Contains(search.ToLower())) ||
|
||||
e.Emails.Any(x => x.Address != null && x.Address.ToLower().Contains(search.ToLower())) ||
|
||||
e.Phones.Any(x => x.Number != null && x.Number.Contains(search))
|
||||
)
|
||||
.Select(e => new
|
||||
{
|
||||
e.Id,
|
||||
e.FirstName,
|
||||
e.LastName,
|
||||
JobTitleName = e.JobTitle != null ? e.JobTitle.Name : "",
|
||||
DepartmentName = e.Department != null ? e.Department.Name : "",
|
||||
e.Status,
|
||||
PrimaryEmail = e.Emails.Where(x => x.IsPrimary == true).Select(x => x.Address).FirstOrDefault(),
|
||||
PrimaryPhone = e.Phones.Where(x => x.IsPrimary == true).Select(x => x.Number).FirstOrDefault(),
|
||||
PhoneExtension = e.Phones.Where(x => x.IsPrimary == true).Select(x => x.Extension).FirstOrDefault()
|
||||
})
|
||||
.OrderByDescending(e => e.Id)
|
||||
.ToList()
|
||||
.Skip((page - 1) * pageSize)
|
||||
.Take(pageSize)
|
||||
.Select(e => new
|
||||
{
|
||||
id = e.Id,
|
||||
firstName = e.FirstName,
|
||||
lastName = e.LastName,
|
||||
jobTitle = e.JobTitleName ?? "",
|
||||
email = e.PrimaryEmail ?? "",
|
||||
phone = e.PrimaryPhone != null
|
||||
? e.PrimaryPhone + (e.PhoneExtension != null ? " ext. " + e.PhoneExtension : "")
|
||||
: "",
|
||||
department = e.DepartmentName ?? "",
|
||||
status = e.Status
|
||||
});
|
||||
|
||||
var totalPages = (int)Math.Ceiling(totalCount / (double)pageSize);
|
||||
var totalPages = (int)Math.Ceiling(pagedResult.TotalCount / (double)pageSize);
|
||||
|
||||
var viewModel = new Pagination_DTO
|
||||
{
|
||||
Data = data,
|
||||
PageNumber = page,
|
||||
PageSize = pageSize,
|
||||
TotalCount = totalCount,
|
||||
TotalCount = pagedResult.TotalCount,
|
||||
TotalPages = totalPages
|
||||
};
|
||||
|
||||
|
|
@ -158,18 +113,8 @@ public class EmployeeController : Controller
|
|||
{
|
||||
try
|
||||
{
|
||||
var employee = await _db.Employees
|
||||
.FirstOrDefaultAsync(e => e.Id == id);
|
||||
|
||||
if (employee == null)
|
||||
return BadRequest(new Response
|
||||
{
|
||||
Status = "Error",
|
||||
Message = "Employee not found."
|
||||
});
|
||||
|
||||
_db.Employees.Remove(employee);
|
||||
await _db.SaveChangesAsync();
|
||||
var userId = User.Identity?.Name ?? "system";
|
||||
await _employeeService.DeleteEmployeeAsync(id, userId);
|
||||
|
||||
return Ok(new DataResponse
|
||||
{
|
||||
|
|
@ -177,6 +122,14 @@ public class EmployeeController : Controller
|
|||
Status = "200"
|
||||
});
|
||||
}
|
||||
catch (InvalidOperationException ex)
|
||||
{
|
||||
return BadRequest(new Response
|
||||
{
|
||||
Status = "Error",
|
||||
Message = ex.Message
|
||||
});
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return BadRequest(new Response
|
||||
|
|
|
|||
|
|
@ -1,87 +1,164 @@
|
|||
using Api.SeaHavenIndustries.DTOs;
|
||||
using System.Security.Claims;
|
||||
using Api.SeaHavenIndustries.DTOs;
|
||||
using Data.SeaHavenIndustries;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Identity;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using SeaHaven.Services.Interfaces;
|
||||
using SeaHaven.Services.DTOs;
|
||||
using SeaHaven.DataServices.Interfaces;
|
||||
|
||||
[ApiController]
|
||||
[Authorize]
|
||||
[Route("api/FollowUp")]
|
||||
[Route("api/followups")]
|
||||
public class FollowUpController : ControllerBase
|
||||
{
|
||||
private readonly ApplicationDbContext _db;
|
||||
private readonly IFollowUpService _followUpService;
|
||||
private readonly IFollowUpDataService _followUpDataService; // Keep for direct entity operations
|
||||
private readonly ApplicationDbContext _db; // Keep for User lookups
|
||||
private readonly UserManager<ApplicationUser> _users;
|
||||
|
||||
public FollowUpController(ApplicationDbContext db)
|
||||
public FollowUpController(IFollowUpService followUpService, IFollowUpDataService followUpDataService, ApplicationDbContext db, UserManager<ApplicationUser> users)
|
||||
{
|
||||
_followUpService = followUpService;
|
||||
_followUpDataService = followUpDataService;
|
||||
_db = db;
|
||||
_users = users;
|
||||
}
|
||||
|
||||
[HttpGet("GetFollowUpList")]
|
||||
public IActionResult GetFollowUps(
|
||||
public async Task<IActionResult> GetFollowUps(
|
||||
string? search = "",
|
||||
bool completed = false,
|
||||
string? status = "Open",
|
||||
int page = 1,
|
||||
int pageSize = 12)
|
||||
{
|
||||
var status = completed ? "Completed" : "Open";
|
||||
status = string.IsNullOrWhiteSpace(status) ? "Open" : status;
|
||||
|
||||
var query = _db.FollowUps
|
||||
.Include(x => x.Employee)
|
||||
.Include(x => x.Account)
|
||||
.Include(x => x.Location)
|
||||
.Include(x => x.WorkOrder)
|
||||
.Where(x => x.Status == status);
|
||||
var pagedResult = await _followUpService.GetFollowUpsPagedAsync(page, pageSize, search, status);
|
||||
var items = pagedResult.Items;
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(search))
|
||||
var rows = items.Select(x => new
|
||||
{
|
||||
search = search.ToLower();
|
||||
Id = x.Id,
|
||||
Reason = x.Reason,
|
||||
ScheduleDate = x.ScheduleDate,
|
||||
ScheduleStartTime = x.ScheduleStartTime,
|
||||
EmployeeId = x.EmployeeId,
|
||||
WorkOrderId = x.WorkOrderId,
|
||||
AccountId = x.AccountId,
|
||||
LocationId = x.LocationId,
|
||||
AccountName = x.AccountName ?? "",
|
||||
AssignedTo = x.EmployeeName ?? "",
|
||||
LocationName = x.LocationName ?? "",
|
||||
WorkOrderNumber = "",
|
||||
InternalWONumber = "",
|
||||
Status = x.Status,
|
||||
CreatedDate = x.CreatedDate,
|
||||
CreatedById = (string?)null
|
||||
}).ToList();
|
||||
|
||||
query = query.Where(x =>
|
||||
(x.Reason ?? "").ToLower().Contains(search) ||
|
||||
(x.Account != null && (x.Account.Name ?? "").ToLower().Contains(search)) ||
|
||||
(x.Employee != null &&
|
||||
(
|
||||
((x.Employee.FirstName ?? "") + " " + (x.Employee.LastName ?? ""))
|
||||
.ToLower()
|
||||
.Contains(search)
|
||||
)
|
||||
)
|
||||
);
|
||||
var creatorIds = rows
|
||||
.Select(r => r.CreatedById)
|
||||
.Where(id => !string.IsNullOrEmpty(id))
|
||||
.Distinct()
|
||||
.ToList();
|
||||
|
||||
var creators = await _db.Users
|
||||
.Where(u => creatorIds.Contains(u.Id))
|
||||
.Select(u => new { u.Id, u.FirstName, u.LastName, u.UserName })
|
||||
.ToDictionaryAsync(u => u.Id, u => u);
|
||||
|
||||
string NameFor(string? userId)
|
||||
{
|
||||
if (string.IsNullOrEmpty(userId) || !creators.TryGetValue(userId, out var u)) return "";
|
||||
var full = ((u.FirstName ?? "") + " " + (u.LastName ?? "")).Trim();
|
||||
return string.IsNullOrWhiteSpace(full) ? (u.UserName ?? "") : full;
|
||||
}
|
||||
|
||||
var totalCount = query.Count();
|
||||
|
||||
var data = query
|
||||
.OrderByDescending(x => x.Id)
|
||||
.Skip((page - 1) * pageSize)
|
||||
.Take(pageSize)
|
||||
.Select(x => new
|
||||
{
|
||||
x.Id,
|
||||
x.Reason,
|
||||
x.ScheduleDate,
|
||||
Account = x.Account != null ? x.Account.Name : "",
|
||||
AssignedTo = x.Employee != null
|
||||
? (x.Employee.FirstName + " " + x.Employee.LastName)
|
||||
: "",
|
||||
LocationName = x.Location != null ? x.Location.Name : "",
|
||||
WorkOrderNumber = x.WorkOrder != null ? x.WorkOrder.WorkerOrderNumber : "",
|
||||
x.Status
|
||||
})
|
||||
.ToList();
|
||||
var data = rows.Select(r => new
|
||||
{
|
||||
r.Id,
|
||||
r.Reason,
|
||||
r.ScheduleDate,
|
||||
r.ScheduleStartTime,
|
||||
r.EmployeeId,
|
||||
r.WorkOrderId,
|
||||
r.AccountId,
|
||||
r.LocationId,
|
||||
r.AccountName,
|
||||
r.AssignedTo,
|
||||
r.LocationName,
|
||||
r.WorkOrderNumber,
|
||||
r.InternalWONumber,
|
||||
r.Status,
|
||||
r.CreatedDate,
|
||||
r.CreatedById,
|
||||
CreatedByName = NameFor(r.CreatedById)
|
||||
}).ToList();
|
||||
|
||||
return Ok(new Pagination_DTO
|
||||
{
|
||||
Data = data,
|
||||
PageNumber = page,
|
||||
PageSize = pageSize,
|
||||
TotalCount = totalCount,
|
||||
TotalPages = (int)Math.Ceiling(totalCount / (double)pageSize)
|
||||
TotalCount = pagedResult.TotalCount,
|
||||
TotalPages = (int)Math.Ceiling(pagedResult.TotalCount / (double)pageSize)
|
||||
});
|
||||
}
|
||||
|
||||
[HttpGet("GetById")]
|
||||
public async Task<IActionResult> GetById(int id)
|
||||
{
|
||||
var model = await _followUpDataService.GetByIdWithDetailsAsync(id);
|
||||
|
||||
if (model == null) return NotFound();
|
||||
|
||||
string createdByName = "";
|
||||
if (!string.IsNullOrEmpty(model.createdby))
|
||||
{
|
||||
var u = await _db.Users
|
||||
.Where(x => x.Id == model.createdby)
|
||||
.Select(x => new { x.FirstName, x.LastName, x.UserName })
|
||||
.FirstOrDefaultAsync();
|
||||
if (u != null)
|
||||
{
|
||||
var n = ((u.FirstName ?? "") + " " + (u.LastName ?? "")).Trim();
|
||||
createdByName = string.IsNullOrWhiteSpace(n) ? (u.UserName ?? "") : n;
|
||||
}
|
||||
}
|
||||
|
||||
return Ok(new
|
||||
{
|
||||
model.Id,
|
||||
model.Reason,
|
||||
model.ScheduleDate,
|
||||
model.ScheduleStartTime,
|
||||
model.EmployeeId,
|
||||
model.WorkOrderId,
|
||||
model.AccountId,
|
||||
model.LocationId,
|
||||
AccountName = model.Account?.Name ?? "",
|
||||
AssignedTo = model.Employee != null
|
||||
? (model.Employee.FirstName + " " + model.Employee.LastName)
|
||||
: "",
|
||||
LocationName = model.Location?.Name ?? "",
|
||||
WorkOrderNumber = model.WorkOrder?.WorkerOrderNumber ?? "",
|
||||
InternalWONumber = model.WorkOrder?.InternalWONumber ?? "",
|
||||
model.Status,
|
||||
model.CreatedDate,
|
||||
CreatedById = model.createdby,
|
||||
CreatedByName = createdByName
|
||||
});
|
||||
}
|
||||
|
||||
[HttpPost("Create")]
|
||||
public async Task<IActionResult> Create(FollowUp_DTO dto)
|
||||
{
|
||||
var userId = User.FindFirstValue(ClaimTypes.NameIdentifier);
|
||||
|
||||
var model = new FollowUps
|
||||
{
|
||||
Reason = dto.Reason,
|
||||
|
|
@ -92,19 +169,19 @@ public class FollowUpController : ControllerBase
|
|||
AccountId = dto.AccountId,
|
||||
LocationId = dto.LocationId,
|
||||
Status = "Open",
|
||||
CreatedDate = DateTime.UtcNow
|
||||
CreatedDate = DateTime.UtcNow,
|
||||
createdby = userId
|
||||
};
|
||||
|
||||
_db.FollowUps.Add(model);
|
||||
await _db.SaveChangesAsync();
|
||||
await _followUpDataService.AddAsync(model);
|
||||
|
||||
return Ok(new DataResponse { Status = "200", Message = "Follow Up created" });
|
||||
return Ok(new DataResponse { Status = "200", Message = "Follow Up created", Data = model.Id });
|
||||
}
|
||||
|
||||
[HttpPost("Update")]
|
||||
public async Task<IActionResult> Update(FollowUp_DTO dto)
|
||||
{
|
||||
var model = await _db.FollowUps.FindAsync(dto.Id);
|
||||
var model = await _followUpDataService.GetByIdAsync(dto.Id);
|
||||
if (model == null) return NotFound();
|
||||
|
||||
model.Reason = dto.Reason;
|
||||
|
|
@ -116,19 +193,19 @@ public class FollowUpController : ControllerBase
|
|||
model.LocationId = dto.LocationId;
|
||||
model.LastModificationTime = DateTime.UtcNow;
|
||||
|
||||
await _db.SaveChangesAsync();
|
||||
await _followUpDataService.UpdateAsync(model);
|
||||
return Ok(new DataResponse { Status = "200", Message = "Follow Up updated" });
|
||||
}
|
||||
|
||||
[HttpPost("Complete")]
|
||||
public async Task<IActionResult> Complete(int id)
|
||||
{
|
||||
var model = await _db.FollowUps.FindAsync(id);
|
||||
var model = await _followUpDataService.GetByIdAsync(id);
|
||||
if (model == null) return NotFound();
|
||||
|
||||
model.Status = "Completed";
|
||||
model.LastModificationTime = DateTime.UtcNow;
|
||||
await _db.SaveChangesAsync();
|
||||
await _followUpDataService.UpdateAsync(model);
|
||||
|
||||
return Ok();
|
||||
}
|
||||
|
|
@ -136,13 +213,24 @@ public class FollowUpController : ControllerBase
|
|||
[HttpPost("Cancel")]
|
||||
public async Task<IActionResult> Cancel(int id)
|
||||
{
|
||||
var model = await _db.FollowUps.FindAsync(id);
|
||||
var model = await _followUpDataService.GetByIdAsync(id);
|
||||
if (model == null) return NotFound();
|
||||
|
||||
model.Status = "Cancelled";
|
||||
model.LastModificationTime = DateTime.UtcNow;
|
||||
await _db.SaveChangesAsync();
|
||||
await _followUpDataService.UpdateAsync(model);
|
||||
|
||||
return Ok();
|
||||
}
|
||||
|
||||
[HttpPost("Delete")]
|
||||
public async Task<IActionResult> Delete(int id)
|
||||
{
|
||||
var model = await _followUpDataService.GetByIdAsync(id);
|
||||
if (model == null) return NotFound();
|
||||
|
||||
await _followUpDataService.DeleteAsync(id);
|
||||
|
||||
return Ok(new DataResponse { Status = "200", Message = "Follow Up deleted" });
|
||||
}
|
||||
}
|
||||
|
|
|
|||
170
Api.SeaHavenIndustries/Controllers/LocationController.cs
Normal file
170
Api.SeaHavenIndustries/Controllers/LocationController.cs
Normal file
|
|
@ -0,0 +1,170 @@
|
|||
using Api.SeaHavenIndustries.DTOs;
|
||||
using Data.SeaHavenIndustries;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using SeaHaven.Services.Interfaces;
|
||||
using SeaHaven.Services.DTOs;
|
||||
using SeaHaven.DataServices.Interfaces;
|
||||
|
||||
namespace Api.SeaHavenIndustries.Controllers
|
||||
{
|
||||
[Authorize]
|
||||
[ApiController]
|
||||
[Route("api/[controller]")]
|
||||
[Route("api/locations")]
|
||||
public class LocationController : Controller
|
||||
{
|
||||
private readonly ILocationService _locationService;
|
||||
private readonly ILocationDataService _locationDataService; // Keep for direct entity operations
|
||||
|
||||
public LocationController(ILocationService locationService, ILocationDataService locationDataService)
|
||||
{
|
||||
_locationService = locationService;
|
||||
_locationDataService = locationDataService;
|
||||
}
|
||||
|
||||
[HttpGet("GetLocationList")]
|
||||
public async Task<IActionResult> GetLocationList(string? search = "", int page = 1, int pageSize = 10)
|
||||
{
|
||||
var pagedResult = await _locationService.GetLocationsPagedAsync(page, pageSize, search);
|
||||
|
||||
var data = pagedResult.Items.Select(l => new
|
||||
{
|
||||
Id = l.Id,
|
||||
Name = l.Name,
|
||||
Title = l.Title,
|
||||
Address = l.Address1,
|
||||
Address1 = l.Address1,
|
||||
Address2 = l.Address2,
|
||||
City = l.City,
|
||||
State = l.State,
|
||||
ZipCode = l.Zip,
|
||||
Phone = l.PhoneNumber,
|
||||
Contact = (string?)null,
|
||||
ContactEmail = l.Email,
|
||||
Status = l.Status
|
||||
});
|
||||
|
||||
var viewModel = new Pagination_DTO
|
||||
{
|
||||
Data = data,
|
||||
PageNumber = page,
|
||||
PageSize = pageSize,
|
||||
TotalCount = pagedResult.TotalCount,
|
||||
TotalPages = (int)Math.Ceiling(pagedResult.TotalCount / (double)pageSize)
|
||||
};
|
||||
|
||||
return Ok(viewModel);
|
||||
}
|
||||
|
||||
[HttpGet("{id}")]
|
||||
public async Task<IActionResult> GetLocationById(int id)
|
||||
{
|
||||
var location = await _locationDataService.GetByIdAsync(id);
|
||||
|
||||
if (location == null)
|
||||
return NotFound(new Response { Status = "Error", Message = "Location not found" });
|
||||
|
||||
var result = new
|
||||
{
|
||||
location.Id,
|
||||
location.Name,
|
||||
location.Title,
|
||||
Address = location.Address1,
|
||||
location.Address1,
|
||||
location.Address2,
|
||||
location.City,
|
||||
location.State,
|
||||
ZipCode = location.Zip,
|
||||
Phone = location.PhoneNumber,
|
||||
Contact = (string?)null,
|
||||
ContactEmail = location.Email,
|
||||
location.Status
|
||||
};
|
||||
|
||||
return Ok(result);
|
||||
}
|
||||
|
||||
[HttpPost]
|
||||
public async Task<IActionResult> AddLocation([FromBody] Location_DTO model)
|
||||
{
|
||||
try
|
||||
{
|
||||
var location = new Locations
|
||||
{
|
||||
Name = model.Name,
|
||||
Title = model.Title,
|
||||
Address1 = model.Address,
|
||||
City = model.City,
|
||||
State = model.State,
|
||||
Zip = model.ZipCode,
|
||||
PhoneNumber = model.Phone,
|
||||
Email = model.ContactEmail,
|
||||
Status = model.Status,
|
||||
CreatedDate = DateTime.Now
|
||||
};
|
||||
|
||||
await _locationDataService.AddAsync(location);
|
||||
return Ok(new DataResponse { Message = "Location Created Successfully", Status = "200" });
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return BadRequest(new Response { Status = "Error", Message = ex.Message });
|
||||
}
|
||||
}
|
||||
|
||||
[HttpPut("{id}")]
|
||||
public async Task<IActionResult> EditLocation(int id, [FromBody] EditLocation_DTO model)
|
||||
{
|
||||
try
|
||||
{
|
||||
var exist = await _locationDataService.GetByIdAsync(id);
|
||||
if (exist == null)
|
||||
return BadRequest(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.PhoneNumber = model.Phone;
|
||||
exist.Email = model.ContactEmail;
|
||||
exist.Status = model.Status;
|
||||
exist.LastModificationTime = DateTime.Now;
|
||||
|
||||
await _locationDataService.UpdateAsync(exist);
|
||||
return Ok(new DataResponse { Message = "Location Updated Successfully", Status = "200" });
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return BadRequest(new Response { Status = "Error", Message = ex.Message });
|
||||
}
|
||||
}
|
||||
|
||||
[HttpDelete("{id}")]
|
||||
public async Task<IActionResult> DeleteLocation(int id)
|
||||
{
|
||||
try
|
||||
{
|
||||
var exist = await _locationDataService.GetByIdAsync(id);
|
||||
if (exist == null)
|
||||
return BadRequest(new Response { Status = "Error", Message = "Location not found" });
|
||||
|
||||
await _locationDataService.DeleteAsync(id);
|
||||
return Ok(new DataResponse { Message = "Location Deleted Successfully", Status = "200" });
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return BadRequest(new Response { Status = "Error", Message = ex.Message });
|
||||
}
|
||||
}
|
||||
|
||||
[HttpGet("DeleteLocation")]
|
||||
public async Task<IActionResult> DeleteLocationByQuery(int id)
|
||||
{
|
||||
return await DeleteLocation(id);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -2,36 +2,36 @@
|
|||
using Data.SeaHavenIndustries;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using SeaHaven.Services.Interfaces;
|
||||
using SeaHaven.Services.DTOs;
|
||||
|
||||
[ApiController]
|
||||
[Route("api/PMSchedule")]
|
||||
[Route("api/pmschedules")]
|
||||
public class PMScheduleController : ControllerBase
|
||||
{
|
||||
private readonly ApplicationDbContext _db;
|
||||
private readonly IPMScheduleService _pmScheduleService;
|
||||
|
||||
public PMScheduleController(ApplicationDbContext db)
|
||||
public PMScheduleController(IPMScheduleService pmScheduleService)
|
||||
{
|
||||
_db = db;
|
||||
_pmScheduleService = pmScheduleService;
|
||||
}
|
||||
|
||||
[HttpGet("GetList")]
|
||||
public IActionResult GetList()
|
||||
public async Task<IActionResult> GetList()
|
||||
{
|
||||
var data = _db.PMSchedules
|
||||
.Include(x => x.Asset)
|
||||
.Include(x => x.Location)
|
||||
.OrderByDescending(x => x.Id)
|
||||
.Select(x => new
|
||||
{
|
||||
x.Id,
|
||||
x.Name,
|
||||
x.Frequency,
|
||||
x.StartDate,
|
||||
x.IsActive,
|
||||
Asset = x.Asset != null ? x.Asset.Name : null,
|
||||
Location = x.Location != null ? x.Location.Name : null
|
||||
})
|
||||
.ToList();
|
||||
var items = await _pmScheduleService.GetAllPMSchedulesAsync();
|
||||
|
||||
var data = items.Select(x => new
|
||||
{
|
||||
Id = x.Id,
|
||||
Name = x.Name,
|
||||
Frequency = x.Frequency,
|
||||
StartDate = x.StartDate,
|
||||
IsActive = x.IsActive,
|
||||
Asset = x.AssetName,
|
||||
Location = x.LocationName
|
||||
});
|
||||
|
||||
return Ok(data);
|
||||
}
|
||||
|
|
@ -39,31 +39,42 @@ public class PMScheduleController : ControllerBase
|
|||
[HttpPost("Save")]
|
||||
public async Task<IActionResult> Save(PMSchedule_DTO dto)
|
||||
{
|
||||
PMSchedules model;
|
||||
|
||||
if (dto.Id == 0)
|
||||
try
|
||||
{
|
||||
model = new PMSchedules
|
||||
var userId = User.Identity?.Name ?? "system";
|
||||
|
||||
if (dto.Id == 0)
|
||||
{
|
||||
CreatedDate = DateTime.UtcNow
|
||||
};
|
||||
_db.PMSchedules.Add(model);
|
||||
var createDto = new CreatePMScheduleDTO
|
||||
{
|
||||
Name = dto.Name,
|
||||
Frequency = dto.Frequency,
|
||||
StartDate = dto.StartDate,
|
||||
IsActive = dto.IsActive,
|
||||
AssetId = dto.AssetId,
|
||||
LocationId = dto.LocationId
|
||||
};
|
||||
await _pmScheduleService.CreatePMScheduleAsync(createDto, userId);
|
||||
}
|
||||
else
|
||||
{
|
||||
var updateDto = new UpdatePMScheduleDTO
|
||||
{
|
||||
Name = dto.Name,
|
||||
Frequency = dto.Frequency,
|
||||
StartDate = dto.StartDate,
|
||||
IsActive = dto.IsActive,
|
||||
AssetId = dto.AssetId,
|
||||
LocationId = dto.LocationId
|
||||
};
|
||||
await _pmScheduleService.UpdatePMScheduleAsync(dto.Id, updateDto, userId);
|
||||
}
|
||||
|
||||
return Ok();
|
||||
}
|
||||
else
|
||||
catch (Exception ex)
|
||||
{
|
||||
model = await _db.PMSchedules.FindAsync(dto.Id);
|
||||
if (model == null) return NotFound();
|
||||
model.LastModificationTime = DateTime.UtcNow;
|
||||
return BadRequest(new { Message = ex.Message });
|
||||
}
|
||||
|
||||
model.Name = dto.Name;
|
||||
model.Frequency = dto.Frequency;
|
||||
model.StartDate = dto.StartDate;
|
||||
model.IsActive = dto.IsActive;
|
||||
model.AssetId = dto.AssetId;
|
||||
model.LocationId = dto.LocationId;
|
||||
|
||||
await _db.SaveChangesAsync();
|
||||
return Ok();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -158,13 +158,13 @@ namespace Api.SeaHavenIndustries.Controllers
|
|||
public IActionResult GetQuotes(string search = "", int page = 1, int pageSize = 10)
|
||||
{
|
||||
var totalCount = _db.Quotes.Count();
|
||||
var data = _db.Quotes.Include(s => s.WorkOrder).AsNoTracking().Where(w => w.QuotId.ToLower().Contains(search.ToLower()) || w.SiteName.ToLower().Contains(search.ToLower()) || w.WorkOrder.WorkerOrderTitle.ToLower().Contains(search.ToLower())).Select(s => new
|
||||
var data = _db.Quotes.Include(s => s.WorkOrder).AsNoTracking().Where(w => (w.QuotId ?? "").ToLower().Contains(search.ToLower()) || (w.SiteName ?? "").ToLower().Contains(search.ToLower()) || (w.WorkOrder != null && (w.WorkOrder.WorkerOrderTitle ?? "").ToLower().Contains(search.ToLower()))).Select(s => new
|
||||
{
|
||||
s.Id,
|
||||
QuoteId = "QO-" + s.QuotId,
|
||||
WorkOrder = s.WorkOrder != null ? s.WorkOrder.WorkerOrderNumber : "",
|
||||
s.ProjectName,
|
||||
TotalAmount = "$" + s.LineItems.Sum(s => s.Lineitem.Value).ToString(),
|
||||
TotalAmount = "$" + (s.LineItems != null ? s.LineItems.Sum(l => l.Lineitem ?? 0) : 0).ToString(),
|
||||
s.SiteName,
|
||||
s.SiteId,
|
||||
s.PO,
|
||||
|
|
@ -174,8 +174,8 @@ namespace Api.SeaHavenIndustries.Controllers
|
|||
s.ContactId,
|
||||
s.Date,
|
||||
lineitems = s.LineItems.Select(q => new { q.Descriptions, q.Lineitem, q.LineitemNumber}),
|
||||
}).OrderByDescending(d => d.Id).ToList().Skip((page - 1) * pageSize)
|
||||
.Take(pageSize);
|
||||
}).OrderByDescending(d => d.Id).Skip((page - 1) * pageSize)
|
||||
.Take(pageSize).ToList();
|
||||
|
||||
// Calculate the number of pages
|
||||
var totalPages = (int)Math.Ceiling(totalCount / (double)pageSize);
|
||||
|
|
@ -201,8 +201,8 @@ namespace Api.SeaHavenIndustries.Controllers
|
|||
s.Id,
|
||||
s.PO,
|
||||
s.TT,
|
||||
s.Locations.Title,
|
||||
s.Locations.Name
|
||||
Title = s.Locations != null ? s.Locations.Title : "",
|
||||
Name = s.Locations != null ? s.Locations.Name : ""
|
||||
}).ToListAsync();
|
||||
return Ok(data);
|
||||
}
|
||||
|
|
@ -216,7 +216,7 @@ namespace Api.SeaHavenIndustries.Controllers
|
|||
s.Id,
|
||||
s.QuotId,
|
||||
s.Date,
|
||||
Name = (s.Contacts.FirstName + " " + s.Contacts.LastName).Trim(),
|
||||
Name = s.Contacts != null ? (s.Contacts.FirstName + " " + s.Contacts.LastName).Trim() : "",
|
||||
s.PO,
|
||||
s.TT,
|
||||
s.SiteName,
|
||||
|
|
@ -225,7 +225,7 @@ namespace Api.SeaHavenIndustries.Controllers
|
|||
s.BillingAddress,
|
||||
s.ShippingAddress,
|
||||
lineitems = s.LineItems.Select(q => new { q.Descriptions, q.Lineitem }),
|
||||
TotalAmount = "$" + s.LineItems.Sum(s => s.Lineitem.Value).ToString()
|
||||
TotalAmount = "$" + (s.LineItems != null ? s.LineItems.Sum(l => l.Lineitem ?? 0) : 0).ToString()
|
||||
}).FirstOrDefaultAsync();
|
||||
return Ok(data);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -47,6 +47,8 @@ namespace Api.SeaHavenIndustries.Controllers
|
|||
try
|
||||
{
|
||||
var exist = _db.Categories.Where(w => w.Id == model.Id).FirstOrDefault();
|
||||
if (exist == null)
|
||||
return BadRequest(new Response { Status = "Error", Message = "Category not found" });
|
||||
exist.Name = model.Name;
|
||||
_db.Categories.Update(exist);
|
||||
await _db.SaveChangesAsync();
|
||||
|
|
@ -102,13 +104,11 @@ namespace Api.SeaHavenIndustries.Controllers
|
|||
{
|
||||
s.Id,
|
||||
s.Name
|
||||
}).OrderByDescending(d => d.Id).ToList().Skip((page - 1) * pageSize)
|
||||
.Take(pageSize);
|
||||
}).OrderByDescending(d => d.Id).Skip((page - 1) * pageSize)
|
||||
.Take(pageSize).ToList();
|
||||
|
||||
// Calculate the number of pages
|
||||
var totalPages = (int)Math.Ceiling(totalCount / (double)pageSize);
|
||||
|
||||
// Create a view model containing the paged items and pagination metadata
|
||||
var viewModel = new Pagination_DTO
|
||||
{
|
||||
Data = data,
|
||||
|
|
@ -127,8 +127,8 @@ namespace Api.SeaHavenIndustries.Controllers
|
|||
try
|
||||
{
|
||||
var totalCount = _db.workOrders.Where(w=> w.istemplate == true).Count();
|
||||
var data = _db.workOrders.Where(w => w.istemplate == true).OrderByDescending(d => d.Id).ToList().Skip((page - 1) * pageSize)
|
||||
.Take(pageSize);
|
||||
var data = _db.workOrders.Where(w => w.istemplate == true).OrderByDescending(d => d.Id).Skip((page - 1) * pageSize)
|
||||
.Take(pageSize).ToList();
|
||||
|
||||
// Calculate the number of pages
|
||||
var totalPages = (int)Math.Ceiling(totalCount / (double)pageSize);
|
||||
|
|
@ -157,8 +157,8 @@ namespace Api.SeaHavenIndustries.Controllers
|
|||
try
|
||||
{
|
||||
var totalCount = _db.Users.Count();
|
||||
var allUsers = _db.Users.Where(w => w.FirstName.ToLower().Contains(search.ToLower())).OrderByDescending(d => d.Id).ToList().Skip((page - 1) * pageSize)
|
||||
.Take(pageSize);
|
||||
var allUsers = _db.Users.Where(w => (w.FirstName ?? "").ToLower().Contains((search ?? "").ToLower())).OrderByDescending(d => d.Id).Skip((page - 1) * pageSize)
|
||||
.Take(pageSize).ToList();
|
||||
|
||||
// Calculate the number of pages
|
||||
var totalPages = (int)Math.Ceiling(totalCount / (double)pageSize);
|
||||
|
|
|
|||
423
Api.SeaHavenIndustries/Controllers/SyncController.cs
Normal file
423
Api.SeaHavenIndustries/Controllers/SyncController.cs
Normal file
|
|
@ -0,0 +1,423 @@
|
|||
using Amazon.DynamoDBv2;
|
||||
using Amazon.DynamoDBv2.Model;
|
||||
using Data.SeaHavenIndustries;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
|
||||
namespace Api.SeaHavenIndustries.Controllers
|
||||
{
|
||||
[Authorize]
|
||||
[ApiController]
|
||||
[Route("api/[controller]")]
|
||||
public class SyncController : Controller
|
||||
{
|
||||
private readonly ApplicationDbContext _db;
|
||||
private readonly AmazonDynamoDBClient _dynamo;
|
||||
private const string WO_TABLE = "WorkOrders";
|
||||
private const string COMMENTS_TABLE = "WorkOrderComments";
|
||||
private const string VENDOR_REPLIES_TABLE = "VendorReplies";
|
||||
|
||||
public SyncController(ApplicationDbContext db)
|
||||
{
|
||||
_db = db;
|
||||
_dynamo = new AmazonDynamoDBClient(Amazon.RegionEndpoint.USEast1);
|
||||
}
|
||||
|
||||
[HttpPost("WorkOrders")]
|
||||
public async Task<IActionResult> SyncWorkOrders()
|
||||
{
|
||||
var synced = 0;
|
||||
var created = 0;
|
||||
var updated = 0;
|
||||
var locationsCreated = 0;
|
||||
|
||||
var lastInternalWO = await _db.workOrders
|
||||
.Where(w => w.InternalWONumber != null && w.InternalWONumber != "")
|
||||
.OrderByDescending(w => w.InternalWONumber)
|
||||
.Select(w => w.InternalWONumber)
|
||||
.FirstOrDefaultAsync();
|
||||
int nextInternal = 10000001;
|
||||
if (lastInternalWO != null && int.TryParse(lastInternalWO, out var parsed))
|
||||
nextInternal = parsed + 1;
|
||||
|
||||
Dictionary<string, AttributeValue>? lastKey = null;
|
||||
|
||||
do
|
||||
{
|
||||
var request = new ScanRequest
|
||||
{
|
||||
TableName = WO_TABLE,
|
||||
Limit = 100,
|
||||
ExclusiveStartKey = lastKey
|
||||
};
|
||||
|
||||
var response = await _dynamo.ScanAsync(request);
|
||||
|
||||
foreach (var item in response.Items)
|
||||
{
|
||||
var externalId = GetString(item, "work_order_id");
|
||||
if (string.IsNullOrEmpty(externalId)) continue;
|
||||
|
||||
var existing = await _db.workOrders
|
||||
.FirstOrDefaultAsync(w => w.ExternalWorkOrderId == externalId);
|
||||
|
||||
var locationId = await ResolveLocationId(
|
||||
GetString(item, "site_code"),
|
||||
GetString(item, "building"),
|
||||
GetString(item, "address"));
|
||||
|
||||
if (locationId.HasValue && !locationsCreated.Equals(0))
|
||||
locationsCreated++;
|
||||
|
||||
if (existing == null)
|
||||
{
|
||||
var wo = new WorkOrder
|
||||
{
|
||||
InternalWONumber = (nextInternal++).ToString("D8"),
|
||||
ExternalWorkOrderId = externalId,
|
||||
WorkerOrderNumber = externalId,
|
||||
WorkerOrderTitle = GetString(item, "description"),
|
||||
Description = GetString(item, "description"),
|
||||
Status = MapStatus(GetString(item, "wo_status")),
|
||||
Priority = MapSeverityToPriority(GetString(item, "severity")),
|
||||
Severity = GetString(item, "severity"),
|
||||
Customer = GetString(item, "customer"),
|
||||
SiteCode = GetString(item, "site_code"),
|
||||
Building = GetString(item, "building"),
|
||||
LocationId = locationId,
|
||||
DueDate = ParseDate(GetString(item, "due_date")),
|
||||
DateReported = ParseDate(GetString(item, "date_reported")),
|
||||
ScheduledStart = ParseDate(GetString(item, "scheduled_start")),
|
||||
AssignTo = null,
|
||||
SourceEmailS3Key = GetString(item, "source_email_s3_key"),
|
||||
CreatedDate = ParseDate(GetString(item, "created_at")) ?? DateTime.UtcNow,
|
||||
istemplate = false
|
||||
};
|
||||
_db.workOrders.Add(wo);
|
||||
created++;
|
||||
}
|
||||
else
|
||||
{
|
||||
existing.WorkerOrderTitle = GetString(item, "description") ?? existing.WorkerOrderTitle;
|
||||
existing.Description = GetString(item, "description") ?? existing.Description;
|
||||
existing.Status = MapStatus(GetString(item, "wo_status")) ?? existing.Status;
|
||||
existing.Priority = MapSeverityToPriority(GetString(item, "severity")) ?? existing.Priority;
|
||||
existing.Severity = GetString(item, "severity") ?? existing.Severity;
|
||||
existing.SiteCode = GetString(item, "site_code") ?? existing.SiteCode;
|
||||
existing.Building = GetString(item, "building") ?? existing.Building;
|
||||
existing.LocationId = locationId ?? existing.LocationId;
|
||||
existing.DueDate = ParseDate(GetString(item, "due_date")) ?? existing.DueDate;
|
||||
existing.DateReported = ParseDate(GetString(item, "date_reported")) ?? existing.DateReported;
|
||||
existing.ScheduledStart = ParseDate(GetString(item, "scheduled_start")) ?? existing.ScheduledStart;
|
||||
existing.SourceEmailS3Key = GetString(item, "source_email_s3_key") ?? existing.SourceEmailS3Key;
|
||||
updated++;
|
||||
}
|
||||
|
||||
synced++;
|
||||
|
||||
if (synced % 100 == 0)
|
||||
await _db.SaveChangesAsync();
|
||||
}
|
||||
|
||||
lastKey = response.LastEvaluatedKey;
|
||||
|
||||
} while (lastKey != null && lastKey.Count > 0);
|
||||
|
||||
await _db.SaveChangesAsync();
|
||||
|
||||
return Ok(new
|
||||
{
|
||||
synced,
|
||||
created,
|
||||
updated,
|
||||
locationsCreated = await _db.Locations.CountAsync()
|
||||
});
|
||||
}
|
||||
|
||||
[HttpPost("Comments")]
|
||||
public async Task<IActionResult> SyncComments()
|
||||
{
|
||||
var synced = 0;
|
||||
var created = 0;
|
||||
var skipped = 0;
|
||||
Dictionary<string, AttributeValue>? lastKey = null;
|
||||
|
||||
do
|
||||
{
|
||||
var request = new ScanRequest
|
||||
{
|
||||
TableName = COMMENTS_TABLE,
|
||||
Limit = 100,
|
||||
ExclusiveStartKey = lastKey
|
||||
};
|
||||
|
||||
var response = await _dynamo.ScanAsync(request);
|
||||
|
||||
foreach (var item in response.Items)
|
||||
{
|
||||
var externalCommentId = GetString(item, "comment_id");
|
||||
var externalWoId = GetString(item, "work_order_id");
|
||||
|
||||
if (string.IsNullOrEmpty(externalCommentId) || string.IsNullOrEmpty(externalWoId))
|
||||
continue;
|
||||
|
||||
var alreadyExists = await _db.Comments
|
||||
.AnyAsync(c => c.ExternalCommentId == externalCommentId);
|
||||
|
||||
if (alreadyExists)
|
||||
{
|
||||
skipped++;
|
||||
synced++;
|
||||
continue;
|
||||
}
|
||||
|
||||
var workOrder = await _db.workOrders
|
||||
.FirstOrDefaultAsync(w => w.ExternalWorkOrderId == externalWoId);
|
||||
|
||||
if (workOrder == null)
|
||||
{
|
||||
skipped++;
|
||||
continue;
|
||||
}
|
||||
|
||||
var comment = new Comments
|
||||
{
|
||||
ExternalCommentId = externalCommentId,
|
||||
WorkerOrderId = workOrder.Id,
|
||||
Commenttext = GetString(item, "text"),
|
||||
Commenter = GetString(item, "commenter"),
|
||||
RecordType = GetString(item, "record_type"),
|
||||
CommentType = "customer",
|
||||
CreatedDate = ParseDate(GetString(item, "created_at")) ?? DateTime.UtcNow,
|
||||
};
|
||||
|
||||
_db.Comments.Add(comment);
|
||||
created++;
|
||||
synced++;
|
||||
|
||||
if (synced % 100 == 0)
|
||||
await _db.SaveChangesAsync();
|
||||
}
|
||||
|
||||
lastKey = response.LastEvaluatedKey;
|
||||
|
||||
} while (lastKey != null && lastKey.Count > 0);
|
||||
|
||||
await _db.SaveChangesAsync();
|
||||
|
||||
return Ok(new { synced, created, skipped });
|
||||
}
|
||||
|
||||
[HttpPost("BackfillInternalWONumbers")]
|
||||
public async Task<IActionResult> BackfillInternalWONumbers()
|
||||
{
|
||||
var wosMissing = await _db.workOrders
|
||||
.Where(w => w.InternalWONumber == null || w.InternalWONumber == "")
|
||||
.OrderBy(w => w.Id)
|
||||
.ToListAsync();
|
||||
|
||||
if (wosMissing.Count == 0)
|
||||
return Ok(new { updated = 0 });
|
||||
|
||||
var lastWO = await _db.workOrders
|
||||
.Where(w => w.InternalWONumber != null && w.InternalWONumber != "")
|
||||
.OrderByDescending(w => w.InternalWONumber)
|
||||
.Select(w => w.InternalWONumber)
|
||||
.FirstOrDefaultAsync();
|
||||
|
||||
int next = 10000001;
|
||||
if (lastWO != null && int.TryParse(lastWO, out var lastNum))
|
||||
next = lastNum + 1;
|
||||
|
||||
foreach (var wo in wosMissing)
|
||||
{
|
||||
wo.InternalWONumber = next.ToString("D8");
|
||||
next++;
|
||||
}
|
||||
|
||||
await _db.SaveChangesAsync();
|
||||
return Ok(new { updated = wosMissing.Count });
|
||||
}
|
||||
|
||||
[HttpPost("BackfillDispatchNumbers")]
|
||||
public async Task<IActionResult> BackfillDispatchNumbers()
|
||||
{
|
||||
var missing = await _db.Dispatches
|
||||
.Where(d => d.DispatchNumber == null || d.DispatchNumber == "")
|
||||
.OrderBy(d => d.Id)
|
||||
.ToListAsync();
|
||||
if (missing.Count == 0) return Ok(new { updated = 0 });
|
||||
|
||||
int next = 1;
|
||||
var last = await _db.Dispatches
|
||||
.Where(d => d.DispatchNumber != null && d.DispatchNumber != "")
|
||||
.OrderByDescending(d => d.DispatchNumber)
|
||||
.Select(d => d.DispatchNumber)
|
||||
.FirstOrDefaultAsync();
|
||||
if (last != null && last.StartsWith("DSP-") && int.TryParse(last.Substring(4), out var n))
|
||||
next = n + 1;
|
||||
|
||||
foreach (var d in missing)
|
||||
{
|
||||
d.DispatchNumber = $"DSP-{next:D5}";
|
||||
next++;
|
||||
}
|
||||
await _db.SaveChangesAsync();
|
||||
return Ok(new { updated = missing.Count });
|
||||
}
|
||||
|
||||
[HttpPost("VendorReplies")]
|
||||
public async Task<IActionResult> SyncVendorReplies()
|
||||
{
|
||||
var synced = 0;
|
||||
var created = 0;
|
||||
var skipped = 0;
|
||||
Dictionary<string, AttributeValue>? lastKey = null;
|
||||
|
||||
do
|
||||
{
|
||||
var request = new ScanRequest
|
||||
{
|
||||
TableName = VENDOR_REPLIES_TABLE,
|
||||
Limit = 100,
|
||||
ExclusiveStartKey = lastKey
|
||||
};
|
||||
|
||||
var response = await _dynamo.ScanAsync(request);
|
||||
|
||||
foreach (var item in response.Items)
|
||||
{
|
||||
var replyId = GetString(item, "reply_id");
|
||||
var woNumber = GetString(item, "internal_wo_number");
|
||||
var dispatchNumber = GetString(item, "dispatch_number");
|
||||
|
||||
if (string.IsNullOrEmpty(replyId) || string.IsNullOrEmpty(woNumber))
|
||||
continue;
|
||||
|
||||
var alreadyExists = await _db.Comments
|
||||
.AnyAsync(c => c.ExternalCommentId == replyId);
|
||||
if (alreadyExists) { skipped++; synced++; continue; }
|
||||
|
||||
var workOrder = await _db.workOrders
|
||||
.FirstOrDefaultAsync(w => w.InternalWONumber == woNumber);
|
||||
if (workOrder == null) { skipped++; continue; }
|
||||
|
||||
int? dispatchId = null;
|
||||
if (!string.IsNullOrWhiteSpace(dispatchNumber))
|
||||
{
|
||||
var dispatch = await _db.Dispatches
|
||||
.FirstOrDefaultAsync(d => d.DispatchNumber == dispatchNumber && d.WorkOrderId == workOrder.Id);
|
||||
dispatchId = dispatch?.Id;
|
||||
}
|
||||
|
||||
var comment = new Comments
|
||||
{
|
||||
ExternalCommentId = replyId,
|
||||
WorkerOrderId = workOrder.Id,
|
||||
DispatchId = dispatchId,
|
||||
Commenttext = GetString(item, "reply_text"),
|
||||
Commenter = GetString(item, "sender_email"),
|
||||
CommentType = "vendor",
|
||||
RecordType = "vendor_reply",
|
||||
CreatedDate = ParseDate(GetString(item, "received_at")) ?? DateTime.UtcNow,
|
||||
};
|
||||
|
||||
_db.Comments.Add(comment);
|
||||
created++;
|
||||
synced++;
|
||||
|
||||
if (synced % 50 == 0)
|
||||
await _db.SaveChangesAsync();
|
||||
}
|
||||
|
||||
lastKey = response.LastEvaluatedKey;
|
||||
} while (lastKey != null && lastKey.Count > 0);
|
||||
|
||||
await _db.SaveChangesAsync();
|
||||
return Ok(new { synced, created, skipped });
|
||||
}
|
||||
|
||||
[HttpPost("BackfillCommentTypes")]
|
||||
public async Task<IActionResult> BackfillCommentTypes()
|
||||
{
|
||||
var updated = await _db.Database.ExecuteSqlRawAsync(
|
||||
"UPDATE Comments SET CommentType = 'customer' WHERE ExternalCommentId IS NOT NULL AND (CommentType IS NULL OR CommentType = '')");
|
||||
return Ok(new { updated });
|
||||
}
|
||||
|
||||
[HttpPost("All")]
|
||||
public async Task<IActionResult> SyncAll()
|
||||
{
|
||||
var woResult = await SyncWorkOrders() as OkObjectResult;
|
||||
var commentResult = await SyncComments() as OkObjectResult;
|
||||
|
||||
return Ok(new
|
||||
{
|
||||
workOrders = woResult?.Value,
|
||||
comments = commentResult?.Value
|
||||
});
|
||||
}
|
||||
|
||||
private async Task<int?> ResolveLocationId(string? siteCode, string? building, string? address)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(siteCode) && string.IsNullOrWhiteSpace(building))
|
||||
return null;
|
||||
|
||||
var matchCode = siteCode ?? building;
|
||||
var existing = await _db.Locations
|
||||
.FirstOrDefaultAsync(l => l.Name == matchCode || l.Title == matchCode);
|
||||
|
||||
if (existing != null)
|
||||
return existing.Id;
|
||||
|
||||
var location = new Locations
|
||||
{
|
||||
Name = matchCode,
|
||||
Title = building,
|
||||
Address1 = address,
|
||||
Status = "Active"
|
||||
};
|
||||
|
||||
_db.Locations.Add(location);
|
||||
await _db.SaveChangesAsync();
|
||||
|
||||
return location.Id;
|
||||
}
|
||||
|
||||
private static string? GetString(Dictionary<string, AttributeValue> item, string key)
|
||||
{
|
||||
if (item.TryGetValue(key, out var val) && val.S != null)
|
||||
return val.S;
|
||||
return null;
|
||||
}
|
||||
|
||||
private static DateTime? ParseDate(string? value)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(value)) return null;
|
||||
if (DateTime.TryParse(value, out var dt)) return dt;
|
||||
return null;
|
||||
}
|
||||
|
||||
private static string? MapStatus(string? dynamoStatus)
|
||||
{
|
||||
return dynamoStatus switch
|
||||
{
|
||||
"new" => "Open",
|
||||
"assigned" => "Open",
|
||||
"in_progress" => "In Progress",
|
||||
"on_hold" => "On Hold",
|
||||
"completed" => "Done",
|
||||
"cancelled" => "Cancelled",
|
||||
"unknown" => "Open",
|
||||
_ => "Open"
|
||||
};
|
||||
}
|
||||
|
||||
private static string? MapSeverityToPriority(string? severity)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(severity)) return null;
|
||||
return $"Sev {severity}";
|
||||
}
|
||||
}
|
||||
}
|
||||
146
Api.SeaHavenIndustries/Controllers/TaskListTemplateController.cs
Normal file
146
Api.SeaHavenIndustries/Controllers/TaskListTemplateController.cs
Normal file
|
|
@ -0,0 +1,146 @@
|
|||
using Api.SeaHavenIndustries.DTOs;
|
||||
using Data.SeaHavenIndustries;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
|
||||
namespace Api.SeaHavenIndustries.Controllers
|
||||
{
|
||||
[Authorize]
|
||||
[ApiController]
|
||||
[Route("api/[controller]")]
|
||||
[Route("api/tasklisttemplates")]
|
||||
public class TaskListTemplateController : Controller
|
||||
{
|
||||
private readonly ApplicationDbContext _db;
|
||||
|
||||
public TaskListTemplateController(ApplicationDbContext db)
|
||||
{
|
||||
_db = db;
|
||||
}
|
||||
|
||||
[HttpGet]
|
||||
public async Task<IActionResult> GetAll()
|
||||
{
|
||||
var data = await _db.TaskListTemplates
|
||||
.Where(t => t.IsActive)
|
||||
.Include(t => t.Items.OrderBy(i => i.SortOrder))
|
||||
.OrderBy(t => t.Name)
|
||||
.Select(t => new
|
||||
{
|
||||
t.Id,
|
||||
t.Name,
|
||||
t.Description,
|
||||
t.IsActive,
|
||||
items = t.Items.OrderBy(i => i.SortOrder).Select(i => new
|
||||
{
|
||||
i.Id,
|
||||
i.ItemText,
|
||||
i.SortOrder
|
||||
}).ToList()
|
||||
})
|
||||
.ToListAsync();
|
||||
return Ok(data);
|
||||
}
|
||||
|
||||
[HttpGet("{id}")]
|
||||
public async Task<IActionResult> GetById(int id)
|
||||
{
|
||||
var template = await _db.TaskListTemplates
|
||||
.Include(t => t.Items.OrderBy(i => i.SortOrder))
|
||||
.Where(t => t.Id == id)
|
||||
.Select(t => new
|
||||
{
|
||||
t.Id,
|
||||
t.Name,
|
||||
t.Description,
|
||||
t.IsActive,
|
||||
items = t.Items.OrderBy(i => i.SortOrder).Select(i => new
|
||||
{
|
||||
i.Id,
|
||||
i.ItemText,
|
||||
i.SortOrder
|
||||
}).ToList()
|
||||
})
|
||||
.FirstOrDefaultAsync();
|
||||
|
||||
if (template == null)
|
||||
return NotFound(new Response { Status = "Error", Message = "Template not found" });
|
||||
|
||||
return Ok(template);
|
||||
}
|
||||
|
||||
[HttpPost]
|
||||
public async Task<IActionResult> Create([FromBody] TaskListTemplate_DTO model)
|
||||
{
|
||||
try
|
||||
{
|
||||
var template = new TaskListTemplate
|
||||
{
|
||||
Name = model.Name,
|
||||
Description = model.Description,
|
||||
IsActive = model.IsActive,
|
||||
Items = model.Items.Select((item, idx) => new TaskListTemplateItem
|
||||
{
|
||||
ItemText = item.ItemText,
|
||||
SortOrder = item.SortOrder > 0 ? item.SortOrder : idx
|
||||
}).ToList()
|
||||
};
|
||||
|
||||
_db.TaskListTemplates.Add(template);
|
||||
await _db.SaveChangesAsync();
|
||||
return Ok(new { message = "Template created", id = template.Id });
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return BadRequest(new Response { Status = "Error", Message = ex.Message });
|
||||
}
|
||||
}
|
||||
|
||||
[HttpPut("{id}")]
|
||||
public async Task<IActionResult> Update(int id, [FromBody] TaskListTemplate_DTO model)
|
||||
{
|
||||
try
|
||||
{
|
||||
var template = await _db.TaskListTemplates
|
||||
.Include(t => t.Items)
|
||||
.FirstOrDefaultAsync(t => t.Id == id);
|
||||
|
||||
if (template == null)
|
||||
return NotFound(new Response { Status = "Error", Message = "Template not found" });
|
||||
|
||||
template.Name = model.Name;
|
||||
template.Description = model.Description;
|
||||
template.IsActive = model.IsActive;
|
||||
|
||||
_db.TaskListTemplateItems.RemoveRange(template.Items);
|
||||
|
||||
template.Items = model.Items.Select((item, idx) => new TaskListTemplateItem
|
||||
{
|
||||
TaskListTemplateId = id,
|
||||
ItemText = item.ItemText,
|
||||
SortOrder = item.SortOrder > 0 ? item.SortOrder : idx
|
||||
}).ToList();
|
||||
|
||||
await _db.SaveChangesAsync();
|
||||
return Ok(new { message = "Template updated" });
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return BadRequest(new Response { Status = "Error", Message = ex.Message });
|
||||
}
|
||||
}
|
||||
|
||||
[HttpDelete("{id}")]
|
||||
public async Task<IActionResult> Delete(int id)
|
||||
{
|
||||
var template = await _db.TaskListTemplates.FindAsync(id);
|
||||
if (template == null)
|
||||
return NotFound(new Response { Status = "Error", Message = "Template not found" });
|
||||
|
||||
template.IsActive = false;
|
||||
await _db.SaveChangesAsync();
|
||||
return Ok(new { message = "Template deleted" });
|
||||
}
|
||||
}
|
||||
}
|
||||
241
Api.SeaHavenIndustries/Controllers/UpliftController.cs
Normal file
241
Api.SeaHavenIndustries/Controllers/UpliftController.cs
Normal file
|
|
@ -0,0 +1,241 @@
|
|||
using Data.SeaHavenIndustries;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using System.Security.Claims;
|
||||
|
||||
namespace Api.SeaHavenIndustries.Controllers
|
||||
{
|
||||
[Authorize]
|
||||
[ApiController]
|
||||
[Route("api/[controller]")]
|
||||
[Route("api/uplifts")]
|
||||
public class UpliftController : Controller
|
||||
{
|
||||
private readonly ApplicationDbContext _db;
|
||||
private readonly IConfiguration _config;
|
||||
|
||||
public UpliftController(ApplicationDbContext db, IConfiguration config)
|
||||
{
|
||||
_db = db;
|
||||
_config = config;
|
||||
}
|
||||
|
||||
[HttpGet]
|
||||
public async Task<IActionResult> List([FromQuery] string? status = "Pending", [FromQuery] int? tier = null, [FromQuery] int page = 1, [FromQuery] int pageSize = 25)
|
||||
{
|
||||
var query = from u in _db.DispatchUpliftRequests
|
||||
join d in _db.Dispatches on u.DispatchId equals d.Id
|
||||
join v in _db.Vendors on d.VendorId equals v.Id into vendors
|
||||
from v in vendors.DefaultIfEmpty()
|
||||
where (u.IsDeleted == null || u.IsDeleted == false)
|
||||
select new { u, d, v };
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(status))
|
||||
query = query.Where(x => x.u.Status == status);
|
||||
if (tier.HasValue)
|
||||
query = query.Where(x => x.u.RequiredTier == tier.Value);
|
||||
|
||||
var total = await query.CountAsync();
|
||||
|
||||
var raw = await query
|
||||
.OrderByDescending(x => x.u.CreatedDate)
|
||||
.Skip((Math.Max(page, 1) - 1) * pageSize)
|
||||
.Take(pageSize)
|
||||
.Select(x => new
|
||||
{
|
||||
x.u.Id,
|
||||
DispatchId = x.d.Id,
|
||||
x.d.DispatchNumber,
|
||||
x.d.PONumber,
|
||||
WorkOrderId = x.d.WorkOrderId,
|
||||
VendorCompanyName = x.v != null ? x.v.CompanyName : x.u.RequestedByVendorName,
|
||||
x.u.CurrentNTE,
|
||||
x.u.RequestedNTE,
|
||||
x.u.VendorReason,
|
||||
x.u.RequiredTier,
|
||||
x.u.Status,
|
||||
RequestedAt = x.u.CreatedDate,
|
||||
x.u.DecidedAt,
|
||||
x.u.DecisionNote
|
||||
})
|
||||
.ToListAsync();
|
||||
|
||||
var items = raw.Select(r => new
|
||||
{
|
||||
r.Id,
|
||||
r.DispatchId,
|
||||
r.DispatchNumber,
|
||||
r.PONumber,
|
||||
r.WorkOrderId,
|
||||
r.VendorCompanyName,
|
||||
r.CurrentNTE,
|
||||
r.RequestedNTE,
|
||||
Delta = r.RequestedNTE - (r.CurrentNTE ?? 0m),
|
||||
r.VendorReason,
|
||||
r.RequiredTier,
|
||||
r.Status,
|
||||
r.RequestedAt,
|
||||
r.DecidedAt,
|
||||
r.DecisionNote,
|
||||
CanDecide = UserCanApprove(r.RequiredTier)
|
||||
}).ToList();
|
||||
|
||||
return Ok(new DataResponse
|
||||
{
|
||||
Status = "Success",
|
||||
Data = new { total, page, pageSize, items }
|
||||
});
|
||||
}
|
||||
|
||||
[HttpGet("dispatch/{dispatchId:int}")]
|
||||
public async Task<IActionResult> ListForDispatch(int dispatchId)
|
||||
{
|
||||
var rows = await (from u in _db.DispatchUpliftRequests
|
||||
where u.DispatchId == dispatchId && (u.IsDeleted == null || u.IsDeleted == false)
|
||||
join dec in _db.Users on u.DecidedByUserId equals dec.Id into decs
|
||||
from dec in decs.DefaultIfEmpty()
|
||||
orderby u.CreatedDate descending
|
||||
select new
|
||||
{
|
||||
u.Id,
|
||||
u.DispatchId,
|
||||
u.CurrentNTE,
|
||||
u.RequestedNTE,
|
||||
Delta = u.RequestedNTE - (u.CurrentNTE ?? 0m),
|
||||
u.VendorReason,
|
||||
u.Status,
|
||||
u.RequiredTier,
|
||||
u.RequestedByVendorName,
|
||||
RequestedAt = u.CreatedDate,
|
||||
u.DecidedAt,
|
||||
u.DecisionNote,
|
||||
DecidedByFirstName = dec != null ? dec.FirstName : null,
|
||||
DecidedByLastName = dec != null ? dec.LastName : null
|
||||
}).ToListAsync();
|
||||
|
||||
var items = rows.Select(r => new
|
||||
{
|
||||
r.Id,
|
||||
r.DispatchId,
|
||||
r.CurrentNTE,
|
||||
r.RequestedNTE,
|
||||
r.Delta,
|
||||
r.VendorReason,
|
||||
r.Status,
|
||||
r.RequiredTier,
|
||||
r.RequestedByVendorName,
|
||||
r.RequestedAt,
|
||||
r.DecidedAt,
|
||||
r.DecisionNote,
|
||||
DecidedByName = string.Join(" ", new[] { r.DecidedByFirstName, r.DecidedByLastName }
|
||||
.Where(s => !string.IsNullOrWhiteSpace(s))).Trim(),
|
||||
CanDecide = UserCanApprove(r.RequiredTier)
|
||||
}).ToList();
|
||||
|
||||
return Ok(new DataResponse { Status = "Success", Data = items });
|
||||
}
|
||||
|
||||
[HttpPost("{id:int}/approve")]
|
||||
public async Task<IActionResult> Approve(int id, [FromBody] DecisionRequest? body)
|
||||
{
|
||||
var req = await _db.DispatchUpliftRequests.FirstOrDefaultAsync(u => u.Id == id);
|
||||
if (req == null) return NotFound(new Response { Status = "Error", Message = "Uplift request not found" });
|
||||
if (req.Status != "Pending") return BadRequest(new Response { Status = "Error", Message = $"Cannot approve a '{req.Status}' request" });
|
||||
if (!UserCanApprove(req.RequiredTier))
|
||||
return StatusCode(403, new Response { Status = "Error", Message = $"Approval requires a Tier {req.RequiredTier} role" });
|
||||
|
||||
var dispatch = await _db.Dispatches.FirstOrDefaultAsync(d => d.Id == req.DispatchId);
|
||||
if (dispatch == null) return NotFound(new Response { Status = "Error", Message = "Dispatch not found" });
|
||||
if (dispatch.Status == "Verified" || dispatch.Status == "Cancelled")
|
||||
return BadRequest(new Response { Status = "Error", Message = $"Cannot approve uplift on a '{dispatch.Status}' dispatch" });
|
||||
|
||||
var userId = User.FindFirst(ClaimTypes.NameIdentifier)?.Value;
|
||||
var now = DateTime.UtcNow;
|
||||
var oldNTE = dispatch.NTEAmount ?? 0m;
|
||||
|
||||
dispatch.NTEAmount = req.RequestedNTE;
|
||||
dispatch.LastModificationTime = now;
|
||||
|
||||
req.Status = "Approved";
|
||||
req.DecidedAt = now;
|
||||
req.DecidedByUserId = userId;
|
||||
req.DecisionNote = string.IsNullOrWhiteSpace(body?.Note) ? null : body!.Note!.Trim();
|
||||
req.LastModificationTime = now;
|
||||
|
||||
_db.WorkOrderAuditLogs.Add(new WorkOrderAuditLog
|
||||
{
|
||||
WorkOrderId = dispatch.WorkOrderId ?? 0,
|
||||
UserId = userId,
|
||||
FieldName = $"Dispatch {dispatch.DispatchNumber} NTE",
|
||||
OldValue = $"${oldNTE:F2}",
|
||||
NewValue = $"${req.RequestedNTE:F2}",
|
||||
Action = "uplift_approved",
|
||||
CreatedAt = now
|
||||
});
|
||||
|
||||
await _db.SaveChangesAsync();
|
||||
return Ok(new DataResponse { Status = "Success", Data = new { req.Id, req.Status, dispatch.NTEAmount } });
|
||||
}
|
||||
|
||||
[HttpPost("{id:int}/deny")]
|
||||
public async Task<IActionResult> Deny(int id, [FromBody] DecisionRequest body)
|
||||
{
|
||||
if (body == null || string.IsNullOrWhiteSpace(body.Note))
|
||||
return BadRequest(new Response { Status = "Error", Message = "A note is required when denying" });
|
||||
|
||||
var req = await _db.DispatchUpliftRequests.FirstOrDefaultAsync(u => u.Id == id);
|
||||
if (req == null) return NotFound(new Response { Status = "Error", Message = "Uplift request not found" });
|
||||
if (req.Status != "Pending") return BadRequest(new Response { Status = "Error", Message = $"Cannot deny a '{req.Status}' request" });
|
||||
if (!UserCanApprove(req.RequiredTier))
|
||||
return StatusCode(403, new Response { Status = "Error", Message = $"Decision requires a Tier {req.RequiredTier} role" });
|
||||
|
||||
var dispatch = await _db.Dispatches.FirstOrDefaultAsync(d => d.Id == req.DispatchId);
|
||||
|
||||
var userId = User.FindFirst(ClaimTypes.NameIdentifier)?.Value;
|
||||
var now = DateTime.UtcNow;
|
||||
|
||||
req.Status = "Denied";
|
||||
req.DecidedAt = now;
|
||||
req.DecidedByUserId = userId;
|
||||
req.DecisionNote = body.Note!.Trim();
|
||||
req.LastModificationTime = now;
|
||||
|
||||
_db.WorkOrderAuditLogs.Add(new WorkOrderAuditLog
|
||||
{
|
||||
WorkOrderId = dispatch?.WorkOrderId ?? 0,
|
||||
UserId = userId,
|
||||
FieldName = $"Dispatch {dispatch?.DispatchNumber} Uplift",
|
||||
OldValue = "Pending",
|
||||
NewValue = "Denied",
|
||||
Action = "uplift_denied",
|
||||
CreatedAt = now
|
||||
});
|
||||
|
||||
await _db.SaveChangesAsync();
|
||||
return Ok(new DataResponse { Status = "Success", Data = new { req.Id, req.Status } });
|
||||
}
|
||||
|
||||
[HttpGet("can-approve")]
|
||||
public IActionResult CanApprove([FromQuery] int tier)
|
||||
{
|
||||
return Ok(new DataResponse { Status = "Success", Data = new { canApprove = UserCanApprove(tier) } });
|
||||
}
|
||||
|
||||
private bool UserCanApprove(int requiredTier)
|
||||
{
|
||||
var key = $"Approvals:Tier{requiredTier}Roles";
|
||||
var roles = _config.GetSection(key).Get<string[]>() ?? Array.Empty<string>();
|
||||
foreach (var r in roles)
|
||||
{
|
||||
if (!string.IsNullOrWhiteSpace(r) && User.IsInRole(r)) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public class DecisionRequest
|
||||
{
|
||||
public string? Note { get; set; }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -94,7 +94,9 @@ namespace Api.SeaHavenIndustries.Controllers
|
|||
else
|
||||
{
|
||||
|
||||
var exist1 = await _userManager.Users.FirstAsync(w => w.Id == model.Id);
|
||||
var exist1 = await _userManager.Users.FirstOrDefaultAsync(w => w.Id == model.Id);
|
||||
if (exist1 == null)
|
||||
return BadRequest(new Response { Status = "Error", Message = "User not found" });
|
||||
|
||||
var existingRole = await _userManager.GetRolesAsync(exist1);
|
||||
|
||||
|
|
@ -134,6 +136,8 @@ namespace Api.SeaHavenIndustries.Controllers
|
|||
try
|
||||
{
|
||||
var exist = _db.Users.Where(w => w.Id == user.Id).FirstOrDefault();
|
||||
if (exist == null)
|
||||
return BadRequest(new Response { Status = "Error", Message = "User not found" });
|
||||
exist.FirstName = user.Name;
|
||||
exist.EmailConfirmed = true;
|
||||
exist.UserName = user.Email;
|
||||
|
|
|
|||
205
Api.SeaHavenIndustries/Controllers/VendorController.cs
Normal file
205
Api.SeaHavenIndustries/Controllers/VendorController.cs
Normal file
|
|
@ -0,0 +1,205 @@
|
|||
using Api.SeaHavenIndustries.DTOs;
|
||||
using Data.SeaHavenIndustries;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using SeaHaven.Services.Interfaces;
|
||||
using SeaHaven.Services.DTOs;
|
||||
|
||||
namespace Api.SeaHavenIndustries.Controllers
|
||||
{
|
||||
[Authorize]
|
||||
[ApiController]
|
||||
[Route("api/[controller]")]
|
||||
[Route("api/vendors")]
|
||||
public class VendorController : Controller
|
||||
{
|
||||
private readonly IVendorService _vendorService;
|
||||
private readonly ApplicationDbContext _db; // Keep for complex queries like nearby vendors
|
||||
private readonly Helper.ZipCodeDistance _zipDistance;
|
||||
private readonly Helper.VendorPortalTokenService _vendorTokens;
|
||||
private readonly IConfiguration _config;
|
||||
|
||||
public VendorController(IVendorService vendorService, ApplicationDbContext db, Helper.ZipCodeDistance zipDistance, Helper.VendorPortalTokenService vendorTokens, IConfiguration config)
|
||||
{
|
||||
_vendorService = vendorService;
|
||||
_db = db;
|
||||
_zipDistance = zipDistance;
|
||||
_vendorTokens = vendorTokens;
|
||||
_config = config;
|
||||
}
|
||||
|
||||
[HttpGet("GetVendorList")]
|
||||
public async Task<IActionResult> GetVendorList(string? search = "", int page = 1, int pageSize = 10)
|
||||
{
|
||||
var pagedResult = await _vendorService.GetVendorsPagedAsync(page, pageSize, search);
|
||||
|
||||
var data = pagedResult.Items.Select(v => new
|
||||
{
|
||||
v.Id,
|
||||
CompanyName = v.Name,
|
||||
v.Email,
|
||||
v.Phone
|
||||
});
|
||||
|
||||
return Ok(new Pagination_DTO
|
||||
{
|
||||
Data = data,
|
||||
PageNumber = page,
|
||||
PageSize = pageSize,
|
||||
TotalCount = pagedResult.TotalCount,
|
||||
TotalPages = (int)Math.Ceiling(pagedResult.TotalCount / (double)pageSize)
|
||||
});
|
||||
}
|
||||
|
||||
[HttpGet("{id}")]
|
||||
public async Task<IActionResult> GetVendorById(int id)
|
||||
{
|
||||
var vendor = await _vendorService.GetVendorByIdAsync(id);
|
||||
|
||||
if (vendor == null)
|
||||
return NotFound(new Response { Status = "Error", Message = "Vendor not found" });
|
||||
|
||||
return Ok(vendor);
|
||||
}
|
||||
|
||||
[HttpPost]
|
||||
public async Task<IActionResult> Create([FromBody] Vendor_DTO model)
|
||||
{
|
||||
try
|
||||
{
|
||||
var createDto = new CreateVendorDTO
|
||||
{
|
||||
Name = model.CompanyName ?? throw new ArgumentException("CompanyName is required")
|
||||
};
|
||||
|
||||
var userId = User.Identity?.Name ?? "system";
|
||||
await _vendorService.CreateVendorAsync(createDto, userId);
|
||||
return Ok(new DataResponse { Message = "Vendor Created", Status = "200" });
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return BadRequest(new Response { Status = "Error", Message = ex.Message });
|
||||
}
|
||||
}
|
||||
|
||||
[HttpPut("{id}")]
|
||||
public async Task<IActionResult> Update(int id, [FromBody] EditVendor_DTO model)
|
||||
{
|
||||
try
|
||||
{
|
||||
var updateDto = new UpdateVendorDTO
|
||||
{
|
||||
Name = model.CompanyName
|
||||
};
|
||||
|
||||
var userId = User.Identity?.Name ?? "system";
|
||||
await _vendorService.UpdateVendorAsync(id, updateDto, userId);
|
||||
return Ok(new DataResponse { Message = "Vendor Updated", Status = "200" });
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return BadRequest(new Response { Status = "Error", Message = ex.Message });
|
||||
}
|
||||
}
|
||||
|
||||
[HttpDelete("{id}")]
|
||||
public async Task<IActionResult> Delete(int id)
|
||||
{
|
||||
var userId = User.Identity?.Name ?? "system";
|
||||
await _vendorService.DeleteVendorAsync(id, userId);
|
||||
return Ok(new DataResponse { Message = "Vendor Deleted", Status = "200" });
|
||||
}
|
||||
|
||||
[HttpGet("Dropdown")]
|
||||
public async Task<IActionResult> GetDropdown([FromQuery] string? trade = null, [FromQuery] string? siteZip = null)
|
||||
{
|
||||
var query = _db.Vendors.Where(v => v.IsActive);
|
||||
|
||||
var vendors = await query.OrderBy(v => v.CompanyName)
|
||||
.Select(v => new
|
||||
{
|
||||
v.Id,
|
||||
v.CompanyName,
|
||||
v.TradeSpecialties,
|
||||
v.Address,
|
||||
v.City,
|
||||
v.State,
|
||||
v.Zip
|
||||
})
|
||||
.ToListAsync();
|
||||
|
||||
var result = vendors.Select(v =>
|
||||
{
|
||||
var distance = _zipDistance.GetDistanceMiles(siteZip, v.Zip);
|
||||
var addr = new[] { v.Address, v.City, v.State, v.Zip }
|
||||
.Where(s => !string.IsNullOrWhiteSpace(s));
|
||||
return new
|
||||
{
|
||||
v.Id,
|
||||
v.CompanyName,
|
||||
v.TradeSpecialties,
|
||||
address = string.Join(", ", addr),
|
||||
distanceMiles = distance.HasValue ? Math.Round(distance.Value, 1) : (double?)null
|
||||
};
|
||||
}).ToList();
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(trade))
|
||||
{
|
||||
var tradeMatched = result.Where(v => (v.TradeSpecialties ?? "").Contains(trade)).ToList();
|
||||
var rest = result.Where(v => !(v.TradeSpecialties ?? "").Contains(trade)).ToList();
|
||||
result = tradeMatched.Concat(rest).ToList();
|
||||
}
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(siteZip))
|
||||
{
|
||||
result = result.OrderBy(v => v.distanceMiles ?? 99999).ToList();
|
||||
}
|
||||
|
||||
return Ok(result);
|
||||
}
|
||||
|
||||
[HttpGet("{id:int}/portal-token")]
|
||||
public async Task<IActionResult> GetPortalToken(int id)
|
||||
{
|
||||
var vendor = await _db.Vendors.FindAsync(id);
|
||||
if (vendor == null) return NotFound();
|
||||
|
||||
var token = await _vendorTokens.GetOrCreateActiveTokenAsync(id);
|
||||
return Ok(BuildPortalTokenResponse(token));
|
||||
}
|
||||
|
||||
[HttpPost("{id:int}/portal-token/rotate")]
|
||||
public async Task<IActionResult> RotatePortalToken(int id)
|
||||
{
|
||||
var vendor = await _db.Vendors.FindAsync(id);
|
||||
if (vendor == null) return NotFound();
|
||||
|
||||
var token = await _vendorTokens.RotateAsync(id);
|
||||
return Ok(BuildPortalTokenResponse(token));
|
||||
}
|
||||
|
||||
[HttpPost("{id:int}/portal-token/revoke")]
|
||||
public async Task<IActionResult> RevokePortalToken(int id)
|
||||
{
|
||||
var vendor = await _db.Vendors.FindAsync(id);
|
||||
if (vendor == null) return NotFound();
|
||||
|
||||
await _vendorTokens.RevokeAllAsync(id);
|
||||
return Ok(new { revoked = true });
|
||||
}
|
||||
|
||||
private object BuildPortalTokenResponse(VendorAccessToken token)
|
||||
{
|
||||
var frontendBase = _config.GetValue<string>("FrontendBaseUrl")?.TrimEnd('/') ?? "";
|
||||
return new
|
||||
{
|
||||
token.Token,
|
||||
token.IssuedAt,
|
||||
token.ExpiresAt,
|
||||
token.LastUsedAt,
|
||||
PortalUrl = $"{frontendBase}/v/{token.Token}/dashboard"
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
696
Api.SeaHavenIndustries/Controllers/VendorPortalController.cs
Normal file
696
Api.SeaHavenIndustries/Controllers/VendorPortalController.cs
Normal file
|
|
@ -0,0 +1,696 @@
|
|||
using Api.SeaHavenIndustries.Helper;
|
||||
using Data.SeaHavenIndustries;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
|
||||
namespace Api.SeaHavenIndustries.Controllers
|
||||
{
|
||||
[ApiController]
|
||||
[Route("api/vendor-portal")]
|
||||
[Route("api/vendorportal")]
|
||||
public class VendorPortalController : Controller
|
||||
{
|
||||
private const string TokenHeader = "X-Vendor-Token";
|
||||
|
||||
private static readonly string[] VendorViewableStatuses =
|
||||
new[] { "Sent", "Acknowledged", "In Progress", "Completed", "Verified", "Cancelled" };
|
||||
|
||||
private readonly ApplicationDbContext _db;
|
||||
private readonly VendorPortalTokenService _tokens;
|
||||
private readonly IConfiguration _config;
|
||||
private readonly SendMessage _sendMessage;
|
||||
|
||||
public VendorPortalController(ApplicationDbContext db, VendorPortalTokenService tokens, IConfiguration config, SendMessage sendMessage)
|
||||
{
|
||||
_db = db;
|
||||
_tokens = tokens;
|
||||
_config = config;
|
||||
_sendMessage = sendMessage;
|
||||
}
|
||||
|
||||
[HttpGet("session")]
|
||||
public async Task<IActionResult> Session()
|
||||
{
|
||||
var vendor = await ResolveVendorAsync();
|
||||
if (vendor == null) return Unauthorized(new Response { Status = "Error", Message = "Invalid or expired token" });
|
||||
|
||||
return Ok(new DataResponse
|
||||
{
|
||||
Status = "Success",
|
||||
Data = new
|
||||
{
|
||||
vendor.Id,
|
||||
vendor.CompanyName,
|
||||
vendor.ContactName,
|
||||
vendor.Email,
|
||||
vendor.Phone
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
[HttpGet("dispatches")]
|
||||
public async Task<IActionResult> ListDispatches([FromQuery] string? status = null)
|
||||
{
|
||||
var vendor = await ResolveVendorAsync();
|
||||
if (vendor == null) return Unauthorized(new Response { Status = "Error", Message = "Invalid or expired token" });
|
||||
|
||||
var query = _db.Dispatches
|
||||
.Include(d => d.WorkOrder).ThenInclude(w => w!.Locations)
|
||||
.Where(d => d.VendorId == vendor.Id && (d.IsDeleted == null || d.IsDeleted == false));
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(status))
|
||||
{
|
||||
query = query.Where(d => d.Status == status);
|
||||
}
|
||||
|
||||
var dispatches = await query
|
||||
.OrderByDescending(d => d.DispatchedAt ?? d.CreatedDate)
|
||||
.Select(d => new
|
||||
{
|
||||
d.Id,
|
||||
d.DispatchNumber,
|
||||
d.PONumber,
|
||||
d.Status,
|
||||
d.NTEAmount,
|
||||
d.ScheduledDate,
|
||||
d.CompletedDate,
|
||||
d.DispatchedAt,
|
||||
d.AcknowledgedAt,
|
||||
WorkOrderTitle = d.WorkOrder!.WorkerOrderTitle,
|
||||
InternalWONumber = d.WorkOrder!.InternalWONumber,
|
||||
LocationName = d.WorkOrder!.Locations != null ? d.WorkOrder!.Locations.Name : null,
|
||||
LocationCity = d.WorkOrder!.Locations != null ? d.WorkOrder!.Locations.City : null,
|
||||
LocationState = d.WorkOrder!.Locations != null ? d.WorkOrder!.Locations.State : null
|
||||
})
|
||||
.ToListAsync();
|
||||
|
||||
return Ok(new DataResponse { Status = "Success", Data = dispatches });
|
||||
}
|
||||
|
||||
[HttpGet("dispatches/{id:int}")]
|
||||
public async Task<IActionResult> GetDispatch(int id)
|
||||
{
|
||||
var vendor = await ResolveVendorAsync();
|
||||
if (vendor == null) return Unauthorized(new Response { Status = "Error", Message = "Invalid or expired token" });
|
||||
|
||||
var dispatch = await LoadVendorDispatchAsync(id, vendor.Id);
|
||||
if (dispatch == null) return NotFound(new Response { Status = "Error", Message = "Dispatch not found" });
|
||||
|
||||
var checklist = await _db.DispatchChecklistItems
|
||||
.Where(c => c.DispatchId == id && (c.IsDeleted == null || c.IsDeleted == false))
|
||||
.OrderBy(c => c.SortOrder)
|
||||
.Select(c => new { c.Id, c.ItemText, c.IsCompleted, c.CompletedBy, c.CompletedAt })
|
||||
.ToListAsync();
|
||||
|
||||
var signoffs = await _db.DispatchSignoffs
|
||||
.Where(s => s.DispatchId == id && (s.IsDeleted == null || s.IsDeleted == false))
|
||||
.Select(s => new { s.Id, s.SignoffType, s.Name, s.SignatureMethod, s.SignedAt })
|
||||
.ToListAsync();
|
||||
|
||||
var rawUplifts = await (from u in _db.DispatchUpliftRequests
|
||||
where u.DispatchId == id && (u.IsDeleted == null || u.IsDeleted == false)
|
||||
join dec in _db.Users on u.DecidedByUserId equals dec.Id into decs
|
||||
from dec in decs.DefaultIfEmpty()
|
||||
orderby u.CreatedDate descending
|
||||
select new
|
||||
{
|
||||
u.Id,
|
||||
u.CurrentNTE,
|
||||
u.RequestedNTE,
|
||||
u.VendorReason,
|
||||
u.Status,
|
||||
u.RequiredTier,
|
||||
u.RequestedByVendorName,
|
||||
u.CreatedDate,
|
||||
u.DecidedAt,
|
||||
u.DecisionNote,
|
||||
DecidedByFirstName = dec != null ? dec.FirstName : null,
|
||||
DecidedByLastName = dec != null ? dec.LastName : null
|
||||
})
|
||||
.ToListAsync();
|
||||
|
||||
var upliftRequests = rawUplifts.Select(u => new
|
||||
{
|
||||
u.Id,
|
||||
u.CurrentNTE,
|
||||
u.RequestedNTE,
|
||||
u.VendorReason,
|
||||
u.Status,
|
||||
u.RequiredTier,
|
||||
u.RequestedByVendorName,
|
||||
RequestedAt = u.CreatedDate,
|
||||
u.DecidedAt,
|
||||
u.DecisionNote,
|
||||
DecidedByName = string.Join(" ", new[] { u.DecidedByFirstName, u.DecidedByLastName }
|
||||
.Where(s => !string.IsNullOrWhiteSpace(s))).Trim()
|
||||
}).ToList();
|
||||
|
||||
var rawComments = await (from c in _db.Comments
|
||||
where c.DispatchId == id
|
||||
&& c.CommentType != "internal"
|
||||
&& (c.IsDeleted == null || c.IsDeleted == false)
|
||||
join u in _db.Users on c.UserId equals u.Id into users
|
||||
from u in users.DefaultIfEmpty()
|
||||
orderby c.CreatedDate
|
||||
select new
|
||||
{
|
||||
c.Id,
|
||||
c.Commenttext,
|
||||
c.Commenter,
|
||||
c.CommentType,
|
||||
c.CreatedDate,
|
||||
c.UserId,
|
||||
UserFirstName = u != null ? u.FirstName : null,
|
||||
UserLastName = u != null ? u.LastName : null
|
||||
})
|
||||
.ToListAsync();
|
||||
|
||||
var comments = rawComments.Select(c =>
|
||||
{
|
||||
var hasShocUser = !string.IsNullOrWhiteSpace(c.UserId);
|
||||
var userName = string.Join(" ", new[] { c.UserFirstName, c.UserLastName }
|
||||
.Where(s => !string.IsNullOrWhiteSpace(s))).Trim();
|
||||
|
||||
string resolvedType;
|
||||
string resolvedCommenter;
|
||||
if (hasShocUser)
|
||||
{
|
||||
resolvedType = "dispatcher";
|
||||
resolvedCommenter = !string.IsNullOrWhiteSpace(userName) ? userName
|
||||
: !string.IsNullOrWhiteSpace(c.Commenter) ? c.Commenter!
|
||||
: "Dispatcher";
|
||||
}
|
||||
else if (c.CommentType == "customer")
|
||||
{
|
||||
resolvedType = "customer";
|
||||
resolvedCommenter = !string.IsNullOrWhiteSpace(c.Commenter) ? c.Commenter! : "Customer";
|
||||
}
|
||||
else
|
||||
{
|
||||
resolvedType = "vendor";
|
||||
resolvedCommenter = !string.IsNullOrWhiteSpace(c.Commenter) ? c.Commenter! : vendor.CompanyName ?? "Vendor";
|
||||
}
|
||||
|
||||
return new
|
||||
{
|
||||
c.Id,
|
||||
c.Commenttext,
|
||||
Commenter = resolvedCommenter,
|
||||
CommentType = resolvedType,
|
||||
c.CreatedDate
|
||||
};
|
||||
}).ToList();
|
||||
|
||||
return Ok(new DataResponse
|
||||
{
|
||||
Status = "Success",
|
||||
Data = new
|
||||
{
|
||||
dispatch.Id,
|
||||
dispatch.DispatchNumber,
|
||||
dispatch.PONumber,
|
||||
dispatch.Status,
|
||||
dispatch.NTEAmount,
|
||||
dispatch.Description,
|
||||
dispatch.ScheduledDate,
|
||||
dispatch.CompletedDate,
|
||||
dispatch.DispatchedAt,
|
||||
dispatch.AcknowledgedAt,
|
||||
WorkOrder = dispatch.WorkOrder == null ? null : new
|
||||
{
|
||||
dispatch.WorkOrder.Id,
|
||||
dispatch.WorkOrder.InternalWONumber,
|
||||
dispatch.WorkOrder.WorkerOrderTitle,
|
||||
dispatch.WorkOrder.Description,
|
||||
dispatch.WorkOrder.Priority,
|
||||
dispatch.WorkOrder.DueDate,
|
||||
dispatch.WorkOrder.Trade,
|
||||
dispatch.WorkOrder.SubTrade,
|
||||
dispatch.WorkOrder.Problem
|
||||
},
|
||||
Location = dispatch.WorkOrder?.Locations == null ? null : new
|
||||
{
|
||||
dispatch.WorkOrder.Locations.Name,
|
||||
dispatch.WorkOrder.Locations.Address1,
|
||||
dispatch.WorkOrder.Locations.City,
|
||||
dispatch.WorkOrder.Locations.State,
|
||||
dispatch.WorkOrder.Locations.Zip
|
||||
},
|
||||
Checklist = checklist,
|
||||
Signoffs = signoffs,
|
||||
Comments = comments,
|
||||
UpliftRequests = upliftRequests
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
[HttpPost("dispatches/{id:int}/accept")]
|
||||
public async Task<IActionResult> Accept(int id)
|
||||
{
|
||||
var vendor = await ResolveVendorAsync();
|
||||
if (vendor == null) return Unauthorized(new Response { Status = "Error", Message = "Invalid or expired token" });
|
||||
|
||||
var dispatch = await LoadVendorDispatchAsync(id, vendor.Id);
|
||||
if (dispatch == null) return NotFound(new Response { Status = "Error", Message = "Dispatch not found" });
|
||||
|
||||
if (dispatch.Status != "Sent")
|
||||
{
|
||||
return BadRequest(new Response { Status = "Error", Message = $"Cannot accept a dispatch with status '{dispatch.Status}'" });
|
||||
}
|
||||
|
||||
var now = DateTime.UtcNow;
|
||||
dispatch.Status = "Acknowledged";
|
||||
dispatch.AcknowledgedAt = now;
|
||||
dispatch.LastModificationTime = now;
|
||||
|
||||
_db.WorkOrderAuditLogs.Add(new WorkOrderAuditLog
|
||||
{
|
||||
WorkOrderId = dispatch.WorkOrderId ?? 0,
|
||||
FieldName = $"Dispatch {dispatch.DispatchNumber} Status",
|
||||
OldValue = "Sent",
|
||||
NewValue = "Acknowledged",
|
||||
Action = "vendor_accept",
|
||||
CreatedAt = now
|
||||
});
|
||||
|
||||
await _db.SaveChangesAsync();
|
||||
return Ok(new DataResponse { Status = "Success", Data = new { dispatch.Id, dispatch.Status, dispatch.AcknowledgedAt } });
|
||||
}
|
||||
|
||||
[HttpPost("dispatches/{id:int}/status")]
|
||||
public async Task<IActionResult> ChangeStatus(int id, [FromBody] ChangeStatusRequest body)
|
||||
{
|
||||
var vendor = await ResolveVendorAsync();
|
||||
if (vendor == null) return Unauthorized(new Response { Status = "Error", Message = "Invalid or expired token" });
|
||||
|
||||
var dispatch = await LoadVendorDispatchAsync(id, vendor.Id);
|
||||
if (dispatch == null) return NotFound(new Response { Status = "Error", Message = "Dispatch not found" });
|
||||
|
||||
var from = dispatch.Status;
|
||||
var to = body.Status;
|
||||
if (!IsAllowedVendorTransition(from, to))
|
||||
{
|
||||
return BadRequest(new Response { Status = "Error", Message = $"Transition from '{from}' to '{to}' is not allowed" });
|
||||
}
|
||||
|
||||
var now = DateTime.UtcNow;
|
||||
dispatch.Status = to;
|
||||
dispatch.LastModificationTime = now;
|
||||
if (to == "Completed") dispatch.CompletedDate = now;
|
||||
|
||||
_db.WorkOrderAuditLogs.Add(new WorkOrderAuditLog
|
||||
{
|
||||
WorkOrderId = dispatch.WorkOrderId ?? 0,
|
||||
FieldName = $"Dispatch {dispatch.DispatchNumber} Status",
|
||||
OldValue = from,
|
||||
NewValue = to,
|
||||
Action = "vendor_status_change",
|
||||
CreatedAt = now
|
||||
});
|
||||
|
||||
await _db.SaveChangesAsync();
|
||||
return Ok(new DataResponse { Status = "Success", Data = new { dispatch.Id, dispatch.Status, dispatch.CompletedDate } });
|
||||
}
|
||||
|
||||
[HttpPost("dispatches/{id:int}/request-cancel")]
|
||||
public async Task<IActionResult> RequestCancel(int id, [FromBody] RequestCancelRequest body)
|
||||
{
|
||||
var vendor = await ResolveVendorAsync();
|
||||
if (vendor == null) return Unauthorized(new Response { Status = "Error", Message = "Invalid or expired token" });
|
||||
|
||||
var dispatch = await LoadVendorDispatchAsync(id, vendor.Id);
|
||||
if (dispatch == null) return NotFound(new Response { Status = "Error", Message = "Dispatch not found" });
|
||||
|
||||
if (dispatch.Status == "Verified" || dispatch.Status == "Cancelled")
|
||||
{
|
||||
return BadRequest(new Response { Status = "Error", Message = $"Cannot request cancel on a '{dispatch.Status}' dispatch" });
|
||||
}
|
||||
|
||||
var now = DateTime.UtcNow;
|
||||
var reason = string.IsNullOrWhiteSpace(body?.Reason) ? "(no reason provided)" : body!.Reason!.Trim();
|
||||
|
||||
_db.Comments.Add(new Comments
|
||||
{
|
||||
DispatchId = id,
|
||||
WorkerOrderId = dispatch.WorkOrderId,
|
||||
Commenter = vendor.CompanyName,
|
||||
CommentType = "vendor",
|
||||
RecordType = "cancel_request",
|
||||
Commenttext = $"Vendor requested cancellation: {reason}",
|
||||
CreatedDate = now
|
||||
});
|
||||
|
||||
_db.WorkOrderAuditLogs.Add(new WorkOrderAuditLog
|
||||
{
|
||||
WorkOrderId = dispatch.WorkOrderId ?? 0,
|
||||
FieldName = $"Dispatch {dispatch.DispatchNumber}",
|
||||
OldValue = dispatch.Status,
|
||||
NewValue = "Cancel Requested",
|
||||
Action = "vendor_request_cancel",
|
||||
CreatedAt = now
|
||||
});
|
||||
|
||||
await _db.SaveChangesAsync();
|
||||
return Ok(new DataResponse { Status = "Success", Message = "Cancel request sent to dispatcher" });
|
||||
}
|
||||
|
||||
[HttpPost("dispatches/{id:int}/checklist/{itemId:int}")]
|
||||
public async Task<IActionResult> UpdateChecklistItem(int id, int itemId, [FromBody] ChecklistUpdateRequest body)
|
||||
{
|
||||
var vendor = await ResolveVendorAsync();
|
||||
if (vendor == null) return Unauthorized(new Response { Status = "Error", Message = "Invalid or expired token" });
|
||||
|
||||
var dispatch = await LoadVendorDispatchAsync(id, vendor.Id);
|
||||
if (dispatch == null) return NotFound(new Response { Status = "Error", Message = "Dispatch not found" });
|
||||
|
||||
if (dispatch.Status == "Verified" || dispatch.Status == "Cancelled")
|
||||
{
|
||||
return BadRequest(new Response { Status = "Error", Message = "Dispatch is locked" });
|
||||
}
|
||||
|
||||
var item = await _db.DispatchChecklistItems
|
||||
.FirstOrDefaultAsync(c => c.Id == itemId && c.DispatchId == id);
|
||||
if (item == null) return NotFound(new Response { Status = "Error", Message = "Checklist item not found" });
|
||||
|
||||
var now = DateTime.UtcNow;
|
||||
item.IsCompleted = body.IsCompleted;
|
||||
item.CompletedBy = body.IsCompleted ? vendor.CompanyName : null;
|
||||
item.CompletedAt = body.IsCompleted ? now : null;
|
||||
item.LastModificationTime = now;
|
||||
|
||||
await _db.SaveChangesAsync();
|
||||
return Ok(new DataResponse
|
||||
{
|
||||
Status = "Success",
|
||||
Data = new { item.Id, item.IsCompleted, item.CompletedBy, item.CompletedAt }
|
||||
});
|
||||
}
|
||||
|
||||
[HttpPost("dispatches/{id:int}/signoff")]
|
||||
public async Task<IActionResult> AddSignoff(int id, [FromBody] SignoffRequest body)
|
||||
{
|
||||
var vendor = await ResolveVendorAsync();
|
||||
if (vendor == null) return Unauthorized(new Response { Status = "Error", Message = "Invalid or expired token" });
|
||||
|
||||
var dispatch = await LoadVendorDispatchAsync(id, vendor.Id);
|
||||
if (dispatch == null) return NotFound(new Response { Status = "Error", Message = "Dispatch not found" });
|
||||
|
||||
if (dispatch.Status != "In Progress" && dispatch.Status != "Completed")
|
||||
{
|
||||
return BadRequest(new Response { Status = "Error", Message = "Signoffs only allowed on In Progress or Completed dispatches" });
|
||||
}
|
||||
|
||||
if (string.IsNullOrWhiteSpace(body?.Name) || string.IsNullOrWhiteSpace(body?.Signature))
|
||||
{
|
||||
return BadRequest(new Response { Status = "Error", Message = "Name and signature are required" });
|
||||
}
|
||||
|
||||
var signoffType = (body?.SignoffType ?? "vendor").Trim().ToLowerInvariant();
|
||||
if (signoffType != "vendor" && signoffType != "customer")
|
||||
{
|
||||
return BadRequest(new Response { Status = "Error", Message = "signoffType must be 'vendor' or 'customer'" });
|
||||
}
|
||||
|
||||
var existing = await _db.DispatchSignoffs
|
||||
.FirstOrDefaultAsync(s => s.DispatchId == id && s.SignoffType == signoffType);
|
||||
if (existing != null)
|
||||
{
|
||||
return BadRequest(new Response { Status = "Error", Message = $"A {signoffType} signoff already exists for this dispatch" });
|
||||
}
|
||||
|
||||
var now = DateTime.UtcNow;
|
||||
var signoff = new DispatchSignoff
|
||||
{
|
||||
DispatchId = id,
|
||||
SignoffType = signoffType,
|
||||
Name = body!.Name,
|
||||
Signature = body.Signature,
|
||||
SignatureMethod = body.SignatureMethod ?? "drawn",
|
||||
SignedAt = now,
|
||||
CreatedDate = now
|
||||
};
|
||||
_db.DispatchSignoffs.Add(signoff);
|
||||
|
||||
_db.WorkOrderAuditLogs.Add(new WorkOrderAuditLog
|
||||
{
|
||||
WorkOrderId = dispatch.WorkOrderId ?? 0,
|
||||
FieldName = $"Dispatch {dispatch.DispatchNumber} Signoff",
|
||||
OldValue = null,
|
||||
NewValue = $"{(signoffType == "customer" ? "Customer" : "Vendor")}: {body.Name}",
|
||||
Action = signoffType == "customer" ? "customer_signoff" : "vendor_signoff",
|
||||
CreatedAt = now
|
||||
});
|
||||
|
||||
await _db.SaveChangesAsync();
|
||||
return Ok(new DataResponse { Status = "Success", Data = new { signoff.Id, signoff.SignedAt } });
|
||||
}
|
||||
|
||||
[HttpPost("dispatches/{id:int}/comments")]
|
||||
public async Task<IActionResult> AddComment(int id, [FromBody] CommentRequest body)
|
||||
{
|
||||
var vendor = await ResolveVendorAsync();
|
||||
if (vendor == null) return Unauthorized(new Response { Status = "Error", Message = "Invalid or expired token" });
|
||||
|
||||
var dispatch = await LoadVendorDispatchAsync(id, vendor.Id);
|
||||
if (dispatch == null) return NotFound(new Response { Status = "Error", Message = "Dispatch not found" });
|
||||
|
||||
if (string.IsNullOrWhiteSpace(body?.CommentText))
|
||||
{
|
||||
return BadRequest(new Response { Status = "Error", Message = "Comment cannot be empty" });
|
||||
}
|
||||
|
||||
var now = DateTime.UtcNow;
|
||||
var comment = new Comments
|
||||
{
|
||||
DispatchId = id,
|
||||
WorkerOrderId = dispatch.WorkOrderId,
|
||||
Commenter = vendor.CompanyName,
|
||||
CommentType = "vendor",
|
||||
RecordType = "comment",
|
||||
Commenttext = body!.CommentText,
|
||||
CreatedDate = now
|
||||
};
|
||||
_db.Comments.Add(comment);
|
||||
await _db.SaveChangesAsync();
|
||||
|
||||
return Ok(new DataResponse
|
||||
{
|
||||
Status = "Success",
|
||||
Data = new { comment.Id, comment.Commenttext, comment.Commenter, comment.CreatedDate }
|
||||
});
|
||||
}
|
||||
|
||||
[HttpPost("dispatches/{id:int}/uplift-request")]
|
||||
public async Task<IActionResult> RequestUplift(int id, [FromBody] UpliftRequestBody body)
|
||||
{
|
||||
var vendor = await ResolveVendorAsync();
|
||||
if (vendor == null) return Unauthorized(new Response { Status = "Error", Message = "Invalid or expired token" });
|
||||
|
||||
var dispatch = await LoadVendorDispatchAsync(id, vendor.Id);
|
||||
if (dispatch == null) return NotFound(new Response { Status = "Error", Message = "Dispatch not found" });
|
||||
|
||||
if (dispatch.Status == "Verified" || dispatch.Status == "Cancelled")
|
||||
{
|
||||
return BadRequest(new Response { Status = "Error", Message = $"Cannot request uplift on a '{dispatch.Status}' dispatch" });
|
||||
}
|
||||
|
||||
if (body == null || body.RequestedNTE <= 0)
|
||||
{
|
||||
return BadRequest(new Response { Status = "Error", Message = "Requested NTE must be greater than zero" });
|
||||
}
|
||||
|
||||
var current = dispatch.NTEAmount ?? 0m;
|
||||
if (body.RequestedNTE <= current)
|
||||
{
|
||||
return BadRequest(new Response { Status = "Error", Message = "Requested NTE must be greater than the current NTE" });
|
||||
}
|
||||
|
||||
var pendingExists = await _db.DispatchUpliftRequests
|
||||
.AnyAsync(u => u.DispatchId == id && u.Status == "Pending" && (u.IsDeleted == null || u.IsDeleted == false));
|
||||
if (pendingExists)
|
||||
{
|
||||
return BadRequest(new Response { Status = "Error", Message = "A pending uplift request already exists for this dispatch" });
|
||||
}
|
||||
|
||||
var tier1Max = _config.GetValue<decimal?>("Approvals:UpliftTier1MaxUsd") ?? 2500m;
|
||||
var delta = body.RequestedNTE - current;
|
||||
var requiredTier = delta > tier1Max ? 2 : 1;
|
||||
|
||||
var now = DateTime.UtcNow;
|
||||
var req = new DispatchUpliftRequest
|
||||
{
|
||||
DispatchId = id,
|
||||
CurrentNTE = current,
|
||||
RequestedNTE = body.RequestedNTE,
|
||||
VendorReason = string.IsNullOrWhiteSpace(body.Reason) ? null : body.Reason!.Trim(),
|
||||
Status = "Pending",
|
||||
RequiredTier = requiredTier,
|
||||
RequestedByVendorName = vendor.CompanyName,
|
||||
CreatedDate = now
|
||||
};
|
||||
_db.DispatchUpliftRequests.Add(req);
|
||||
|
||||
_db.WorkOrderAuditLogs.Add(new WorkOrderAuditLog
|
||||
{
|
||||
WorkOrderId = dispatch.WorkOrderId ?? 0,
|
||||
FieldName = $"Dispatch {dispatch.DispatchNumber} Uplift",
|
||||
OldValue = $"${current:F2}",
|
||||
NewValue = $"${body.RequestedNTE:F2}",
|
||||
Action = "uplift_requested",
|
||||
CreatedAt = now
|
||||
});
|
||||
|
||||
await _db.SaveChangesAsync();
|
||||
|
||||
await NotifyDispatcherOfUpliftAsync(dispatch, req, vendor);
|
||||
|
||||
return Ok(new DataResponse
|
||||
{
|
||||
Status = "Success",
|
||||
Data = new { req.Id, req.Status, req.RequiredTier, req.CurrentNTE, req.RequestedNTE }
|
||||
});
|
||||
}
|
||||
|
||||
[HttpPost("dispatches/{id:int}/uplift-request/{requestId:int}/cancel")]
|
||||
public async Task<IActionResult> CancelUpliftRequest(int id, int requestId)
|
||||
{
|
||||
var vendor = await ResolveVendorAsync();
|
||||
if (vendor == null) return Unauthorized(new Response { Status = "Error", Message = "Invalid or expired token" });
|
||||
|
||||
var dispatch = await LoadVendorDispatchAsync(id, vendor.Id);
|
||||
if (dispatch == null) return NotFound(new Response { Status = "Error", Message = "Dispatch not found" });
|
||||
|
||||
var req = await _db.DispatchUpliftRequests
|
||||
.FirstOrDefaultAsync(u => u.Id == requestId && u.DispatchId == id);
|
||||
if (req == null) return NotFound(new Response { Status = "Error", Message = "Uplift request not found" });
|
||||
|
||||
if (req.Status != "Pending")
|
||||
{
|
||||
return BadRequest(new Response { Status = "Error", Message = $"Cannot cancel a '{req.Status}' uplift request" });
|
||||
}
|
||||
|
||||
var now = DateTime.UtcNow;
|
||||
req.Status = "Cancelled";
|
||||
req.DecidedAt = now;
|
||||
req.LastModificationTime = now;
|
||||
|
||||
_db.WorkOrderAuditLogs.Add(new WorkOrderAuditLog
|
||||
{
|
||||
WorkOrderId = dispatch.WorkOrderId ?? 0,
|
||||
FieldName = $"Dispatch {dispatch.DispatchNumber} Uplift",
|
||||
OldValue = "Pending",
|
||||
NewValue = "Cancelled",
|
||||
Action = "uplift_cancelled",
|
||||
CreatedAt = now
|
||||
});
|
||||
|
||||
await _db.SaveChangesAsync();
|
||||
return Ok(new DataResponse { Status = "Success", Data = new { req.Id, req.Status } });
|
||||
}
|
||||
|
||||
private async Task NotifyDispatcherOfUpliftAsync(Dispatch dispatch, DispatchUpliftRequest req, Vendor vendor)
|
||||
{
|
||||
try
|
||||
{
|
||||
var dispatchAudit = await _db.WorkOrderAuditLogs
|
||||
.Where(a => a.WorkOrderId == (dispatch.WorkOrderId ?? 0) && a.Action == "dispatch" && !string.IsNullOrEmpty(a.UserId))
|
||||
.OrderByDescending(a => a.CreatedAt)
|
||||
.FirstOrDefaultAsync();
|
||||
if (dispatchAudit == null || string.IsNullOrWhiteSpace(dispatchAudit.UserId)) return;
|
||||
|
||||
var dispatcher = await _db.Users.FindAsync(dispatchAudit.UserId);
|
||||
if (dispatcher == null || string.IsNullOrWhiteSpace(dispatcher.Email)) return;
|
||||
|
||||
var frontendBase = _config.GetValue<string>("FrontendBaseUrl")?.TrimEnd('/') ?? "";
|
||||
var workOrderLink = dispatch.WorkOrderId.HasValue
|
||||
? $"{frontendBase}/workorders/{dispatch.WorkOrderId.Value}"
|
||||
: frontendBase;
|
||||
|
||||
var vendorName = System.Net.WebUtility.HtmlEncode(vendor.CompanyName ?? "Vendor");
|
||||
var reason = System.Net.WebUtility.HtmlEncode(req.VendorReason ?? "(no reason provided)");
|
||||
var dispatchNum = System.Net.WebUtility.HtmlEncode(dispatch.DispatchNumber ?? "");
|
||||
var tierText = req.RequiredTier == 2 ? "Tier 2 (Manager approval required)" : "Tier 1";
|
||||
|
||||
var subject = $"[Uplift Request] {dispatch.DispatchNumber} — {vendor.CompanyName} requests ${req.RequestedNTE:F2} (was ${req.CurrentNTE ?? 0m:F2})";
|
||||
var body = $@"
|
||||
<h2>Vendor Uplift Request</h2>
|
||||
<p><strong>Dispatch:</strong> {dispatchNum}</p>
|
||||
<p><strong>Vendor:</strong> {vendorName}</p>
|
||||
<table style='border-collapse:collapse;font-family:Arial,sans-serif;'>
|
||||
<tr><td style='padding:4px 10px;'><strong>Current NTE</strong></td><td style='padding:4px 10px;'>${req.CurrentNTE ?? 0m:F2}</td></tr>
|
||||
<tr><td style='padding:4px 10px;'><strong>Requested NTE</strong></td><td style='padding:4px 10px;'>${req.RequestedNTE:F2}</td></tr>
|
||||
<tr><td style='padding:4px 10px;'><strong>Delta</strong></td><td style='padding:4px 10px;'>${(req.RequestedNTE - (req.CurrentNTE ?? 0m)):F2}</td></tr>
|
||||
<tr><td style='padding:4px 10px;'><strong>Required Approval</strong></td><td style='padding:4px 10px;'>{tierText}</td></tr>
|
||||
</table>
|
||||
<h3>Vendor Reason</h3>
|
||||
<p>{reason}</p>
|
||||
<div style='margin:20px 0;'>
|
||||
<a href='{workOrderLink}' style='display:inline-block;padding:10px 22px;background:#2563eb;color:white;text-decoration:none;border-radius:6px;font-weight:bold;'>Review in SHOC</a>
|
||||
</div>";
|
||||
|
||||
await _sendMessage.SendEMail(dispatcher.Email, subject, body);
|
||||
}
|
||||
catch
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
private async Task<Vendor?> ResolveVendorAsync()
|
||||
{
|
||||
if (!Request.Headers.TryGetValue(TokenHeader, out var values)) return null;
|
||||
var token = values.ToString();
|
||||
return await _tokens.ResolveVendorAsync(token);
|
||||
}
|
||||
|
||||
private Task<Dispatch?> LoadVendorDispatchAsync(int id, int vendorId)
|
||||
{
|
||||
return _db.Dispatches
|
||||
.Include(d => d.WorkOrder).ThenInclude(w => w!.Locations)
|
||||
.FirstOrDefaultAsync(d => d.Id == id
|
||||
&& d.VendorId == vendorId
|
||||
&& (d.IsDeleted == null || d.IsDeleted == false));
|
||||
}
|
||||
|
||||
private static bool IsAllowedVendorTransition(string? from, string? to)
|
||||
{
|
||||
if (from == "Acknowledged" && to == "In Progress") return true;
|
||||
if (from == "In Progress" && to == "Completed") return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
public class ChangeStatusRequest
|
||||
{
|
||||
public string? Status { get; set; }
|
||||
}
|
||||
|
||||
public class RequestCancelRequest
|
||||
{
|
||||
public string? Reason { get; set; }
|
||||
}
|
||||
|
||||
public class ChecklistUpdateRequest
|
||||
{
|
||||
public bool IsCompleted { get; set; }
|
||||
}
|
||||
|
||||
public class SignoffRequest
|
||||
{
|
||||
public string? Name { get; set; }
|
||||
public string? Signature { get; set; }
|
||||
public string? SignatureMethod { get; set; }
|
||||
public string? SignoffType { get; set; }
|
||||
}
|
||||
|
||||
public class CommentRequest
|
||||
{
|
||||
public string? CommentText { get; set; }
|
||||
}
|
||||
|
||||
public class UpliftRequestBody
|
||||
{
|
||||
public decimal RequestedNTE { get; set; }
|
||||
public string? Reason { get; set; }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,23 +0,0 @@
|
|||
using Microsoft.AspNetCore.Mvc;
|
||||
|
||||
namespace Api.SeaHavenIndustries.Controllers
|
||||
{
|
||||
[ApiController]
|
||||
[Route("[controller]")]
|
||||
public class WeatherForecastController : ControllerBase
|
||||
{
|
||||
private static readonly string[] Summaries = new[]
|
||||
{
|
||||
"Freezing", "Bracing", "Chilly", "Cool", "Mild", "Warm", "Balmy", "Hot", "Sweltering", "Scorching"
|
||||
};
|
||||
|
||||
private readonly ILogger<WeatherForecastController> _logger;
|
||||
|
||||
public WeatherForecastController(ILogger<WeatherForecastController> logger)
|
||||
{
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load diff
|
|
@ -2,10 +2,11 @@
|
|||
{
|
||||
public class Comments_DTO
|
||||
{
|
||||
public string Text { get; set; }
|
||||
public string? Text { get; set; }
|
||||
public IFormFile? Document { get; set; }
|
||||
public int WorkorderId { get; set; }
|
||||
public DateTime CreatedDate { get; set; }
|
||||
public string UserId { get; set; }
|
||||
public DateTime? CreatedDate { get; set; }
|
||||
public string? UserId { get; set; }
|
||||
public string? CommentType { get; set; }
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,7 @@
|
|||
namespace Api.SeaHavenIndustries.DTOs
|
||||
using Data.SeaHavenIndustries;
|
||||
using SeaHaven.Services.DTOs;
|
||||
|
||||
namespace Api.SeaHavenIndustries.DTOs
|
||||
{
|
||||
public class Contact_DTO
|
||||
{
|
||||
|
|
@ -20,5 +23,100 @@
|
|||
public string? TwitterUrl { get; set; }
|
||||
public int? LocationId { get; set; }
|
||||
public int? AccountId { get; set; }
|
||||
|
||||
// ✅ Map API DTO to Service DTO
|
||||
public CreateContactDTO ToServiceCreateDTO()
|
||||
{
|
||||
return new CreateContactDTO
|
||||
{
|
||||
FirstName = this.FirstName,
|
||||
LastName = this.LastName,
|
||||
Email = this.Email ?? "",
|
||||
Phone = this.PhoneNumber,
|
||||
Title = this.Title,
|
||||
Type = this.ContactType,
|
||||
AccountId = this.AccountId
|
||||
};
|
||||
}
|
||||
|
||||
// Map from Entity to DTO (Model)
|
||||
public static Contact_DTO MapToModel(Contacts entity)
|
||||
{
|
||||
return new Contact_DTO
|
||||
{
|
||||
Title = entity.Title,
|
||||
Owner = entity.Owner,
|
||||
FirstName = entity.FirstName,
|
||||
MiddleName = entity.MiddleName,
|
||||
LastName = entity.LastName,
|
||||
ContactType = entity.ContactType,
|
||||
PhoneNumber = entity.PhoneNumber,
|
||||
Email = entity.Email,
|
||||
Address1 = entity.Address1,
|
||||
Address2 = entity.Address2,
|
||||
City = entity.City,
|
||||
State = entity.State,
|
||||
Zip = entity.Zip,
|
||||
FacebookUrl = entity.FacebookUrl,
|
||||
LinkedInUrl = entity.LinkedInUrl,
|
||||
TwitterUrl = entity.TwitterUrl,
|
||||
LocationId = entity.LocationId,
|
||||
AccountId = entity.AccountId
|
||||
};
|
||||
}
|
||||
|
||||
// Map from DTO (Model) to Entity
|
||||
public static Contacts MapToEntity(Contact_DTO dto, Contacts? existingEntity = null)
|
||||
{
|
||||
var entity = existingEntity ?? new Contacts();
|
||||
|
||||
entity.Title = dto.Title;
|
||||
entity.Owner = dto.Owner;
|
||||
entity.FirstName = dto.FirstName;
|
||||
entity.MiddleName = dto.MiddleName;
|
||||
entity.LastName = dto.LastName;
|
||||
entity.ContactType = dto.ContactType;
|
||||
entity.PhoneNumber = dto.PhoneNumber;
|
||||
entity.Email = dto.Email;
|
||||
entity.Address1 = dto.Address1;
|
||||
entity.Address2 = dto.Address2;
|
||||
entity.City = dto.City;
|
||||
entity.State = dto.State;
|
||||
entity.Zip = dto.Zip;
|
||||
entity.FacebookUrl = dto.FacebookUrl;
|
||||
entity.LinkedInUrl = dto.LinkedInUrl;
|
||||
entity.TwitterUrl = dto.TwitterUrl;
|
||||
entity.LocationId = dto.LocationId;
|
||||
entity.AccountId = dto.AccountId;
|
||||
|
||||
return entity;
|
||||
}
|
||||
}
|
||||
|
||||
// DTO for contact list view
|
||||
public class ContactListItem_DTO
|
||||
{
|
||||
public int Id { get; set; }
|
||||
public string? Name { get; set; }
|
||||
public string? Email { get; set; }
|
||||
public string? Phone { get; set; }
|
||||
|
||||
// ✅ Map from Service DTO
|
||||
public static ContactListItem_DTO FromServiceDTO(ContactDTO serviceDto)
|
||||
{
|
||||
return new ContactListItem_DTO
|
||||
{
|
||||
Id = serviceDto.Id,
|
||||
Name = (serviceDto.FirstName + " " + serviceDto.LastName).Trim(),
|
||||
Email = serviceDto.Email,
|
||||
Phone = serviceDto.Phone
|
||||
};
|
||||
}
|
||||
|
||||
// ✅ Map collection from Service DTOs
|
||||
public static IEnumerable<ContactListItem_DTO> FromServiceDTOs(IEnumerable<ContactDTO> serviceDtos)
|
||||
{
|
||||
return serviceDtos.Select(FromServiceDTO);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
11
Api.SeaHavenIndustries/DTOs/DispatchSignoff_DTO.cs
Normal file
11
Api.SeaHavenIndustries/DTOs/DispatchSignoff_DTO.cs
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
namespace Api.SeaHavenIndustries.DTOs
|
||||
{
|
||||
public class DispatchSignoff_DTO
|
||||
{
|
||||
public int DispatchId { get; set; }
|
||||
public string? SignoffType { get; set; }
|
||||
public string? Name { get; set; }
|
||||
public string? Signature { get; set; }
|
||||
public string? SignatureMethod { get; set; }
|
||||
}
|
||||
}
|
||||
27
Api.SeaHavenIndustries/DTOs/Dispatch_DTO.cs
Normal file
27
Api.SeaHavenIndustries/DTOs/Dispatch_DTO.cs
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
namespace Api.SeaHavenIndustries.DTOs
|
||||
{
|
||||
public class Dispatch_DTO
|
||||
{
|
||||
public List<int> WorkOrderIds { get; set; } = new();
|
||||
public List<int> VendorIds { get; set; } = new();
|
||||
public decimal? NTEAmount { get; set; }
|
||||
public DateTime? ScheduledDate { get; set; }
|
||||
public string? Description { get; set; }
|
||||
public int? TaskListTemplateId { get; set; }
|
||||
public List<string>? CustomChecklistItems { get; set; }
|
||||
}
|
||||
|
||||
public class ChecklistItemUpdate_DTO
|
||||
{
|
||||
public int Id { get; set; }
|
||||
public bool IsCompleted { get; set; }
|
||||
public string? CompletedBy { get; set; }
|
||||
}
|
||||
|
||||
public class AddChecklistItem_DTO
|
||||
{
|
||||
public int DispatchId { get; set; }
|
||||
public int? WorkOrderId { get; set; }
|
||||
public string? ItemText { get; set; }
|
||||
}
|
||||
}
|
||||
|
|
@ -1,4 +1,7 @@
|
|||
namespace Api.SeaHavenIndustries.DTOs
|
||||
using Data.SeaHavenIndustries;
|
||||
using SeaHaven.Services.DTOs;
|
||||
|
||||
namespace Api.SeaHavenIndustries.DTOs
|
||||
{
|
||||
public class EditContact_DTO
|
||||
{
|
||||
|
|
@ -21,5 +24,72 @@
|
|||
public string? TwitterUrl { get; set; }
|
||||
public int? LocationId { get; set; }
|
||||
public int? AccountId { get; set; }
|
||||
|
||||
// ✅ Map API DTO to Service DTO
|
||||
public UpdateContactDTO ToServiceUpdateDTO()
|
||||
{
|
||||
return new UpdateContactDTO
|
||||
{
|
||||
FirstName = this.FirstName,
|
||||
LastName = this.LastName,
|
||||
Email = this.Email,
|
||||
Phone = this.PhoneNumber,
|
||||
Title = this.Title,
|
||||
Type = this.ContactType,
|
||||
AccountId = this.AccountId
|
||||
};
|
||||
}
|
||||
|
||||
// Map from Entity to DTO (Model)
|
||||
public static EditContact_DTO MapToModel(Contacts entity)
|
||||
{
|
||||
return new EditContact_DTO
|
||||
{
|
||||
Id = entity.Id,
|
||||
Title = entity.Title,
|
||||
Owner = entity.Owner,
|
||||
FirstName = entity.FirstName,
|
||||
MiddleName = entity.MiddleName,
|
||||
LastName = entity.LastName,
|
||||
ContactType = entity.ContactType,
|
||||
PhoneNumber = entity.PhoneNumber,
|
||||
Email = entity.Email,
|
||||
Address1 = entity.Address1,
|
||||
Address2 = entity.Address2,
|
||||
City = entity.City,
|
||||
State = entity.State,
|
||||
Zip = entity.Zip,
|
||||
FacebookUrl = entity.FacebookUrl,
|
||||
LinkedInUrl = entity.LinkedInUrl,
|
||||
TwitterUrl = entity.TwitterUrl,
|
||||
LocationId = entity.LocationId,
|
||||
AccountId = entity.AccountId
|
||||
};
|
||||
}
|
||||
|
||||
// Map from DTO (Model) to Entity - updates existing entity
|
||||
public static Contacts MapToEntity(EditContact_DTO dto, Contacts existingEntity)
|
||||
{
|
||||
existingEntity.Title = dto.Title;
|
||||
existingEntity.Owner = dto.Owner;
|
||||
existingEntity.FirstName = dto.FirstName;
|
||||
existingEntity.MiddleName = dto.MiddleName;
|
||||
existingEntity.LastName = dto.LastName;
|
||||
existingEntity.ContactType = dto.ContactType;
|
||||
existingEntity.PhoneNumber = dto.PhoneNumber;
|
||||
existingEntity.Email = dto.Email;
|
||||
existingEntity.Address1 = dto.Address1;
|
||||
existingEntity.Address2 = dto.Address2;
|
||||
existingEntity.City = dto.City;
|
||||
existingEntity.State = dto.State;
|
||||
existingEntity.Zip = dto.Zip;
|
||||
existingEntity.FacebookUrl = dto.FacebookUrl;
|
||||
existingEntity.LinkedInUrl = dto.LinkedInUrl;
|
||||
existingEntity.TwitterUrl = dto.TwitterUrl;
|
||||
existingEntity.LocationId = dto.LocationId;
|
||||
existingEntity.AccountId = dto.AccountId;
|
||||
|
||||
return existingEntity;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,9 +1,37 @@
|
|||
namespace Api.SeaHavenIndustries.DTOs
|
||||
using SeaHaven.Services.DTOs;
|
||||
|
||||
namespace Api.SeaHavenIndustries.DTOs
|
||||
{
|
||||
public class EditLocation_DTO
|
||||
{
|
||||
public int Id { get; set; }
|
||||
public string? Title { get; set; }
|
||||
public string? Name { get; set; }
|
||||
public string? Address { get; set; }
|
||||
public string? City { get; set; }
|
||||
public string? State { get; set; }
|
||||
public string? ZipCode { get; set; }
|
||||
public string? Phone { get; set; }
|
||||
public string? Contact { get; set; }
|
||||
public string? ContactEmail { get; set; }
|
||||
public string? Status { get; set; }
|
||||
public string? AccountId { get; set; }
|
||||
|
||||
public int? GetAccountId() =>
|
||||
int.TryParse(AccountId, out var id) ? id : null;
|
||||
|
||||
// ✅ Map API DTO to Service DTO
|
||||
public UpdateLocationDTO ToServiceUpdateDTO()
|
||||
{
|
||||
return new UpdateLocationDTO
|
||||
{
|
||||
LocationName = this.Name,
|
||||
Address = this.Address,
|
||||
City = this.City,
|
||||
State = this.State,
|
||||
Zipcode = this.ZipCode,
|
||||
AccountId = this.GetAccountId()
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,8 +1,135 @@
|
|||
namespace Api.SeaHavenIndustries.DTOs
|
||||
using Data.SeaHavenIndustries;
|
||||
using SeaHaven.Services.DTOs;
|
||||
|
||||
namespace Api.SeaHavenIndustries.DTOs
|
||||
{
|
||||
public class Location_DTO
|
||||
{
|
||||
public string? Title { get; set; }
|
||||
public string? Name { get; set; }
|
||||
public string? Address { get; set; }
|
||||
public string? City { get; set; }
|
||||
public string? State { get; set; }
|
||||
public string? ZipCode { get; set; }
|
||||
public string? Phone { get; set; }
|
||||
public string? Contact { get; set; }
|
||||
public string? ContactEmail { get; set; }
|
||||
public string? Status { get; set; }
|
||||
public string? AccountId { get; set; }
|
||||
|
||||
public int? GetAccountId() =>
|
||||
int.TryParse(AccountId, out var id) ? id : null;
|
||||
|
||||
// ✅ Map API DTO to Service DTO
|
||||
public CreateLocationDTO ToServiceCreateDTO()
|
||||
{
|
||||
return new CreateLocationDTO
|
||||
{
|
||||
LocationName = this.Name ?? "",
|
||||
Address = this.Address,
|
||||
City = this.City,
|
||||
State = this.State,
|
||||
Zipcode = this.ZipCode,
|
||||
AccountId = this.GetAccountId()
|
||||
};
|
||||
}
|
||||
|
||||
// Map from Entity to DTO
|
||||
public static Location_DTO MapToModel(Locations entity)
|
||||
{
|
||||
return new Location_DTO
|
||||
{
|
||||
Title = entity.Title,
|
||||
Name = entity.Name,
|
||||
Address = entity.Address1,
|
||||
City = entity.City,
|
||||
State = entity.State,
|
||||
ZipCode = entity.Zip,
|
||||
Phone = entity.PhoneNumber,
|
||||
Contact = null, // Not in database schema
|
||||
ContactEmail = entity.Email,
|
||||
Status = entity.Status,
|
||||
AccountId = null // Not in database schema
|
||||
};
|
||||
}
|
||||
|
||||
// Map from DTO to Entity
|
||||
public static Locations MapToEntity(Location_DTO dto, Locations? existingEntity = null)
|
||||
{
|
||||
var entity = existingEntity ?? new Locations();
|
||||
|
||||
entity.Title = dto.Title;
|
||||
entity.Name = dto.Name;
|
||||
entity.Address1 = dto.Address;
|
||||
entity.City = dto.City;
|
||||
entity.State = dto.State;
|
||||
entity.Zip = dto.ZipCode;
|
||||
entity.PhoneNumber = dto.Phone;
|
||||
// Contact field doesn't exist in database schema - ignored
|
||||
entity.Email = dto.ContactEmail;
|
||||
entity.Status = dto.Status;
|
||||
// AccountId field doesn't exist in database schema - ignored
|
||||
|
||||
return entity;
|
||||
}
|
||||
}
|
||||
|
||||
// DTO for list views
|
||||
public class LocationListItem_DTO
|
||||
{
|
||||
public int Id { get; set; }
|
||||
public string? Name { get; set; }
|
||||
public string? Title { get; set; }
|
||||
|
||||
// Map from Entity
|
||||
public static LocationListItem_DTO MapToModel(Locations entity)
|
||||
{
|
||||
return new LocationListItem_DTO
|
||||
{
|
||||
Id = entity.Id,
|
||||
Name = entity.Name,
|
||||
Title = entity.Title
|
||||
};
|
||||
}
|
||||
|
||||
// ✅ Map from Service DTO (no logic in controller)
|
||||
public static LocationListItem_DTO FromServiceDTO(LocationDTO serviceDto)
|
||||
{
|
||||
return new LocationListItem_DTO
|
||||
{
|
||||
Id = serviceDto.Id,
|
||||
Name = serviceDto.Name ?? serviceDto.Title,
|
||||
Title = serviceDto.Title ?? serviceDto.Name
|
||||
};
|
||||
}
|
||||
|
||||
// ✅ Map collection from Service DTOs
|
||||
public static IEnumerable<LocationListItem_DTO> FromServiceDTOs(IEnumerable<LocationDTO> serviceDtos)
|
||||
{
|
||||
return serviceDtos.Select(FromServiceDTO);
|
||||
}
|
||||
}
|
||||
|
||||
// DTO for location dropdown/simple view
|
||||
public class LocationViewModel_DTO
|
||||
{
|
||||
public int? Id { get; set; }
|
||||
public string? Name { get; set; }
|
||||
|
||||
// ✅ Map from Service DTO
|
||||
public static LocationViewModel_DTO FromServiceDTO(LocationDTO serviceDto)
|
||||
{
|
||||
return new LocationViewModel_DTO
|
||||
{
|
||||
Id = serviceDto.Id,
|
||||
Name = serviceDto.Name ?? serviceDto.Title
|
||||
};
|
||||
}
|
||||
|
||||
// ✅ Map collection from Service DTOs
|
||||
public static IEnumerable<LocationViewModel_DTO> FromServiceDTOs(IEnumerable<LocationDTO> serviceDtos)
|
||||
{
|
||||
return serviceDtos.Select(FromServiceDTO);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
16
Api.SeaHavenIndustries/DTOs/TaskListTemplate_DTO.cs
Normal file
16
Api.SeaHavenIndustries/DTOs/TaskListTemplate_DTO.cs
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
namespace Api.SeaHavenIndustries.DTOs
|
||||
{
|
||||
public class TaskListTemplate_DTO
|
||||
{
|
||||
public string? Name { get; set; }
|
||||
public string? Description { get; set; }
|
||||
public bool IsActive { get; set; } = true;
|
||||
public List<TaskListTemplateItem_DTO> Items { get; set; } = new();
|
||||
}
|
||||
|
||||
public class TaskListTemplateItem_DTO
|
||||
{
|
||||
public string? ItemText { get; set; }
|
||||
public int SortOrder { get; set; }
|
||||
}
|
||||
}
|
||||
19
Api.SeaHavenIndustries/DTOs/UpdateDispatch_DTO.cs
Normal file
19
Api.SeaHavenIndustries/DTOs/UpdateDispatch_DTO.cs
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
namespace Api.SeaHavenIndustries.DTOs
|
||||
{
|
||||
public class UpdateDispatch_DTO
|
||||
{
|
||||
public int Id { get; set; }
|
||||
public string? Status { get; set; }
|
||||
public decimal? NTEAmount { get; set; }
|
||||
public DateTime? ScheduledDate { get; set; }
|
||||
public DateTime? CompletedDate { get; set; }
|
||||
public string? Description { get; set; }
|
||||
}
|
||||
|
||||
public class DispatchComment_DTO
|
||||
{
|
||||
public int DispatchId { get; set; }
|
||||
public string? Text { get; set; }
|
||||
public bool SendEmail { get; set; } = true;
|
||||
}
|
||||
}
|
||||
21
Api.SeaHavenIndustries/DTOs/Vendor_DTO.cs
Normal file
21
Api.SeaHavenIndustries/DTOs/Vendor_DTO.cs
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
namespace Api.SeaHavenIndustries.DTOs
|
||||
{
|
||||
public class Vendor_DTO
|
||||
{
|
||||
public string? CompanyName { get; set; }
|
||||
public string? ContactName { get; set; }
|
||||
public string? Email { get; set; }
|
||||
public string? Phone { get; set; }
|
||||
public string? Address { get; set; }
|
||||
public string? City { get; set; }
|
||||
public string? State { get; set; }
|
||||
public string? Zip { get; set; }
|
||||
public string? TradeSpecialties { get; set; }
|
||||
public bool IsActive { get; set; } = true;
|
||||
}
|
||||
|
||||
public class EditVendor_DTO : Vendor_DTO
|
||||
{
|
||||
public int Id { get; set; }
|
||||
}
|
||||
}
|
||||
|
|
@ -44,12 +44,18 @@ 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 string? Status { get; set; }
|
||||
public int? LocationId { get; set; }
|
||||
//public int? CategoryId { get; set; }
|
||||
public string? Problem { get; set; }
|
||||
public string? Trade { get; set; }
|
||||
public string? SubTrade { get; set; }
|
||||
public decimal? VendorNTE { get; set; }
|
||||
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; }
|
||||
|
|
|
|||
|
|
@ -98,8 +98,27 @@ namespace Api.SeaHavenIndustries.Helper
|
|||
return false;
|
||||
}
|
||||
}
|
||||
public async Task<bool> SendDispatchEmail(string emailTo, string subject, string htmlBody, string? replyTo)
|
||||
{
|
||||
try
|
||||
{
|
||||
var apiKey = _configuration.GetValue<string>("SendGrid:ApiKey");
|
||||
var client = new SendGridClient(apiKey);
|
||||
var from = new EmailAddress("tech@seahavenind.com", "Sea Haven Industries");
|
||||
var to = new EmailAddress(emailTo, "");
|
||||
|
||||
var msg = MailHelper.CreateSingleEmail(from, to, subject, "", htmlBody);
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(replyTo))
|
||||
msg.SetReplyTo(new EmailAddress(replyTo));
|
||||
|
||||
var response = await client.SendEmailAsync(msg);
|
||||
return true;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
|
|
|||
92
Api.SeaHavenIndustries/Helper/VendorPortalTokenService.cs
Normal file
92
Api.SeaHavenIndustries/Helper/VendorPortalTokenService.cs
Normal file
|
|
@ -0,0 +1,92 @@
|
|||
using System.Security.Cryptography;
|
||||
using Data.SeaHavenIndustries;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
|
||||
namespace Api.SeaHavenIndustries.Helper
|
||||
{
|
||||
public class VendorPortalTokenService
|
||||
{
|
||||
private readonly ApplicationDbContext _db;
|
||||
private readonly int _lifetimeDays;
|
||||
|
||||
public VendorPortalTokenService(ApplicationDbContext db, IConfiguration config)
|
||||
{
|
||||
_db = db;
|
||||
_lifetimeDays = config.GetValue<int?>("VendorPortal:TokenLifetimeDays") ?? 365;
|
||||
}
|
||||
|
||||
public async Task<VendorAccessToken> GetOrCreateActiveTokenAsync(int vendorId)
|
||||
{
|
||||
var now = DateTime.UtcNow;
|
||||
var existing = await _db.VendorAccessTokens
|
||||
.Where(t => t.VendorId == vendorId
|
||||
&& t.RevokedAt == null
|
||||
&& t.ExpiresAt > now
|
||||
&& (t.IsDeleted == null || t.IsDeleted == false))
|
||||
.OrderByDescending(t => t.IssuedAt)
|
||||
.FirstOrDefaultAsync();
|
||||
|
||||
if (existing != null) return existing;
|
||||
|
||||
var token = new VendorAccessToken
|
||||
{
|
||||
VendorId = vendorId,
|
||||
Token = GenerateToken(),
|
||||
IssuedAt = now,
|
||||
ExpiresAt = now.AddDays(_lifetimeDays),
|
||||
CreatedDate = now
|
||||
};
|
||||
_db.VendorAccessTokens.Add(token);
|
||||
await _db.SaveChangesAsync();
|
||||
return token;
|
||||
}
|
||||
|
||||
public async Task<Vendor?> ResolveVendorAsync(string? token)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(token)) return null;
|
||||
var now = DateTime.UtcNow;
|
||||
var record = await _db.VendorAccessTokens
|
||||
.Include(t => t.Vendor)
|
||||
.FirstOrDefaultAsync(t => t.Token == token
|
||||
&& t.RevokedAt == null
|
||||
&& t.ExpiresAt > now
|
||||
&& (t.IsDeleted == null || t.IsDeleted == false));
|
||||
|
||||
if (record?.Vendor == null) return null;
|
||||
|
||||
record.LastUsedAt = now;
|
||||
await _db.SaveChangesAsync();
|
||||
return record.Vendor;
|
||||
}
|
||||
|
||||
public async Task<VendorAccessToken> RotateAsync(int vendorId)
|
||||
{
|
||||
var now = DateTime.UtcNow;
|
||||
var active = await _db.VendorAccessTokens
|
||||
.Where(t => t.VendorId == vendorId && t.RevokedAt == null)
|
||||
.ToListAsync();
|
||||
foreach (var t in active) t.RevokedAt = now;
|
||||
await _db.SaveChangesAsync();
|
||||
return await GetOrCreateActiveTokenAsync(vendorId);
|
||||
}
|
||||
|
||||
public async Task RevokeAllAsync(int vendorId)
|
||||
{
|
||||
var now = DateTime.UtcNow;
|
||||
var active = await _db.VendorAccessTokens
|
||||
.Where(t => t.VendorId == vendorId && t.RevokedAt == null)
|
||||
.ToListAsync();
|
||||
foreach (var t in active) t.RevokedAt = now;
|
||||
await _db.SaveChangesAsync();
|
||||
}
|
||||
|
||||
private static string GenerateToken()
|
||||
{
|
||||
var bytes = RandomNumberGenerator.GetBytes(32);
|
||||
return Convert.ToBase64String(bytes)
|
||||
.Replace("+", "-")
|
||||
.Replace("/", "_")
|
||||
.TrimEnd('=');
|
||||
}
|
||||
}
|
||||
}
|
||||
48
Api.SeaHavenIndustries/Helper/ZipCodeDistance.cs
Normal file
48
Api.SeaHavenIndustries/Helper/ZipCodeDistance.cs
Normal file
|
|
@ -0,0 +1,48 @@
|
|||
namespace Api.SeaHavenIndustries.Helper
|
||||
{
|
||||
public class ZipCodeDistance
|
||||
{
|
||||
private readonly Dictionary<string, (double lat, double lng)> _coords = new();
|
||||
private bool _loaded;
|
||||
|
||||
public void Load(string csvPath)
|
||||
{
|
||||
if (_loaded) return;
|
||||
foreach (var line in File.ReadLines(csvPath).Skip(1))
|
||||
{
|
||||
var parts = line.Split(',');
|
||||
if (parts.Length >= 3
|
||||
&& double.TryParse(parts[1].Trim(), out var lat)
|
||||
&& double.TryParse(parts[2].Trim(), out var lng))
|
||||
{
|
||||
_coords[parts[0].Trim().PadLeft(5, '0')] = (lat, lng);
|
||||
}
|
||||
}
|
||||
_loaded = true;
|
||||
}
|
||||
|
||||
public double? GetDistanceMiles(string? zip1, string? zip2)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(zip1) || string.IsNullOrWhiteSpace(zip2)) return null;
|
||||
var z1 = zip1.Trim().PadLeft(5, '0');
|
||||
var z2 = zip2.Trim().PadLeft(5, '0');
|
||||
if (z1.Length > 5) z1 = z1.Substring(0, 5);
|
||||
if (z2.Length > 5) z2 = z2.Substring(0, 5);
|
||||
if (!_coords.TryGetValue(z1, out var c1) || !_coords.TryGetValue(z2, out var c2)) return null;
|
||||
return Haversine(c1.lat, c1.lng, c2.lat, c2.lng);
|
||||
}
|
||||
|
||||
private static double Haversine(double lat1, double lon1, double lat2, double lon2)
|
||||
{
|
||||
const double R = 3958.8;
|
||||
var dLat = ToRad(lat2 - lat1);
|
||||
var dLon = ToRad(lon2 - lon1);
|
||||
var a = Math.Sin(dLat / 2) * Math.Sin(dLat / 2) +
|
||||
Math.Cos(ToRad(lat1)) * Math.Cos(ToRad(lat2)) *
|
||||
Math.Sin(dLon / 2) * Math.Sin(dLon / 2);
|
||||
return R * 2 * Math.Atan2(Math.Sqrt(a), Math.Sqrt(1 - a));
|
||||
}
|
||||
|
||||
private static double ToRad(double deg) => deg * Math.PI / 180.0;
|
||||
}
|
||||
}
|
||||
|
|
@ -31,7 +31,49 @@ 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 =>
|
||||
{
|
||||
var zipService = new Api.SeaHavenIndustries.Helper.ZipCodeDistance();
|
||||
var env = sp.GetRequiredService<IWebHostEnvironment>();
|
||||
var csvPath = Path.Combine(env.WebRootPath, "zipcodes.csv");
|
||||
if (File.Exists(csvPath)) zipService.Load(csvPath);
|
||||
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.AddAuthentication(options =>
|
||||
{
|
||||
options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
|
||||
|
|
@ -93,7 +135,8 @@ if (app.Environment.IsDevelopment() || app.Environment.IsProduction())
|
|||
});
|
||||
}
|
||||
|
||||
app.UseHttpsRedirection();
|
||||
if (!app.Environment.IsDevelopment())
|
||||
app.UseHttpsRedirection();
|
||||
app.UseStaticFiles();
|
||||
app.UseCors();
|
||||
app.UseAuthentication();
|
||||
|
|
@ -101,4 +144,60 @@ app.UseAuthorization();
|
|||
|
||||
app.MapControllers();
|
||||
|
||||
//if (app.Environment.IsDevelopment())
|
||||
//{
|
||||
// using var scope = app.Services.CreateScope();
|
||||
// var userManager = scope.ServiceProvider.GetRequiredService<UserManager<ApplicationUser>>();
|
||||
// var roleManager = scope.ServiceProvider.GetRequiredService<RoleManager<IdentityRole>>();
|
||||
|
||||
// if (!await roleManager.RoleExistsAsync("Admin"))
|
||||
// await roleManager.CreateAsync(new IdentityRole("Admin"));
|
||||
// if (!await roleManager.RoleExistsAsync("Dispatcher"))
|
||||
// await roleManager.CreateAsync(new IdentityRole("Dispatcher"));
|
||||
// if (!await roleManager.RoleExistsAsync("Manager"))
|
||||
// await roleManager.CreateAsync(new IdentityRole("Manager"));
|
||||
|
||||
// var adminEmail = "admin@seahavenind.com";
|
||||
// if (await userManager.FindByNameAsync(adminEmail) == null)
|
||||
// {
|
||||
// var admin = new ApplicationUser
|
||||
// {
|
||||
// UserName = adminEmail,
|
||||
// Email = adminEmail,
|
||||
// FirstName = "Admin",
|
||||
// LastName = "User",
|
||||
// EmailConfirmed = true,
|
||||
// UniqueName = "Active",
|
||||
// CreatedDate = DateTime.Now
|
||||
// };
|
||||
// var result = await userManager.CreateAsync(admin, "Admin@123");
|
||||
// if (result.Succeeded)
|
||||
// await userManager.AddToRoleAsync(admin, "Admin");
|
||||
// }
|
||||
|
||||
// var db = scope.ServiceProvider.GetRequiredService<ApplicationDbContext>();
|
||||
// if (!db.DropdownOptions.Any())
|
||||
// {
|
||||
// var trades = new[] { "Plumbing", "Electrical", "HVAC", "Fire/Life Safety", "General", "Roofing", "Janitorial", "Painting", "Locksmith", "Carpentry" };
|
||||
// for (int i = 0; i < trades.Length; i++)
|
||||
// db.DropdownOptions.Add(new DropdownOption { Category = "Trade", Value = trades[i], SortOrder = i });
|
||||
|
||||
// var subTrades = new (string trade, string sub)[] {
|
||||
// ("Plumbing", "Drain Cleaning"), ("Plumbing", "Pipe Repair"), ("Plumbing", "Fixture Install"), ("Plumbing", "Backflow Testing"),
|
||||
// ("Electrical", "Lighting"), ("Electrical", "Panel/Breaker"), ("Electrical", "Wiring"), ("Electrical", "Generator"),
|
||||
// ("HVAC", "AC Repair"), ("HVAC", "Heating"), ("HVAC", "Ductwork"), ("HVAC", "Controls/BMS"),
|
||||
// ("Fire/Life Safety", "Sprinkler"), ("Fire/Life Safety", "Fire Alarm"), ("Fire/Life Safety", "Extinguisher"), ("Fire/Life Safety", "Fire Pump"),
|
||||
// ("General", "Doors"), ("General", "Flooring"), ("General", "Drywall"), ("General", "Concrete"),
|
||||
// };
|
||||
// for (int i = 0; i < subTrades.Length; i++)
|
||||
// db.DropdownOptions.Add(new DropdownOption { Category = "SubTrade", Value = subTrades[i].sub, ParentValue = subTrades[i].trade, SortOrder = i });
|
||||
|
||||
// var problems = new[] { "Leak", "No Power", "Not Cooling", "Not Heating", "Clogged", "Broken/Damaged", "Alarm Fault", "Preventive Maintenance", "Inspection", "Installation", "Other" };
|
||||
// for (int i = 0; i < problems.Length; i++)
|
||||
// db.DropdownOptions.Add(new DropdownOption { Category = "Problem", Value = problems[i], SortOrder = i });
|
||||
|
||||
// await db.SaveChangesAsync();
|
||||
// }
|
||||
//}
|
||||
|
||||
app.Run();
|
||||
|
|
|
|||
|
|
@ -1,8 +1,26 @@
|
|||
{
|
||||
"ConnectionStrings": {
|
||||
//"DefaultConnection": "Server=DESKTOP-64LC14O\SQLEXPRESS;Database=SeahavenIndustries;TrustServerCertificate=True;Trusted_Connection=True;MultipleActiveResultSets=true",
|
||||
|
||||
// Test database
|
||||
//"DefaultConnection": "Server=SQL5112.site4now.net;Initial Catalog=db_a7141e_seahavenindustries;User Id=db_a7141e_seahavenindustries_admin;Password=Abc123!@#;MultipleActiveResultSets=true"
|
||||
|
||||
//Client Database
|
||||
"DefaultConnection": "Server=SQL5112.site4now.net;Initial Catalog=db_a7141e_seahavenindustry;User Id=db_a7141e_seahavenindustry_admin;Password=Abc123!@#;MultipleActiveResultSets=true"
|
||||
},
|
||||
"Logging": {
|
||||
"LogLevel": {
|
||||
"Default": "Information",
|
||||
"Microsoft.AspNetCore": "Warning"
|
||||
}
|
||||
},
|
||||
"SendGrid": {
|
||||
"ApiKey": "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"
|
||||
},
|
||||
"AllowedHosts": "*",
|
||||
"JWT": {
|
||||
"ValidAudience": "http://localhost:4200",
|
||||
"ValidIssuer": "http://localhost:7195",
|
||||
"Secret": "JWTAuthenticationHIGHsecuredPasswordVVVp1OH7Xzyr"
|
||||
}
|
||||
}
|
||||
}
|
||||
20
Api.SeaHavenIndustries/appsettings.Production.json
Normal file
20
Api.SeaHavenIndustries/appsettings.Production.json
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
{
|
||||
"ConnectionStrings": {
|
||||
"DefaultConnection": "Server=SQL5112.site4now.net;Initial Catalog=db_a7141e_seahavenindustry;User Id=db_a7141e_seahavenindustry_admin;Password=Abc123!@#;MultipleActiveResultSets=true"
|
||||
},
|
||||
"Logging": {
|
||||
"LogLevel": {
|
||||
"Default": "Information",
|
||||
"Microsoft.AspNetCore": "Warning"
|
||||
}
|
||||
},
|
||||
"SendGrid": {
|
||||
"ApiKey": "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"
|
||||
},
|
||||
"AllowedHosts": "*",
|
||||
"JWT": {
|
||||
"ValidAudience": "http://console.seahavenind.com",
|
||||
"ValidIssuer": "http://console.seahavenind.com",
|
||||
"Secret": "JWTAuthenticationHIGHsecuredPasswordVVVp1OH7Xzyr"
|
||||
}
|
||||
}
|
||||
|
|
@ -23,4 +23,4 @@
|
|||
"ValidIssuer": "http://localhost:7195",
|
||||
"Secret": "JWTAuthenticationHIGHsecuredPasswordVVVp1OH7Xzyr"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,13 +1,12 @@
|
|||
|
||||
{
|
||||
"region" : "us-east-1",
|
||||
"configuration" : "Debug",
|
||||
"framework" : "net8.0",
|
||||
"self-contained" : true,
|
||||
"application" : "SeaHavenIndustries",
|
||||
"environment" : "SeaHavenIndustries-dev",
|
||||
"enable-xray" : false,
|
||||
"enhanced-health-type" : "basic",
|
||||
"additional-options" : "",
|
||||
"proxy-server" : "nginx"
|
||||
"region": "us-east-1",
|
||||
"configuration": "Debug",
|
||||
"framework": "net8.0",
|
||||
"self-contained": true,
|
||||
"application": "SeaHavenIndustries",
|
||||
"environment": "SeaHavenIndustries-dev",
|
||||
"enable-xray": false,
|
||||
"enhanced-health-type": "basic",
|
||||
"additional-options": "",
|
||||
"proxy-server": "nginx"
|
||||
}
|
||||
|
|
@ -924,6 +924,206 @@ PRIMARY KEY CLUSTERED
|
|||
GO
|
||||
|
||||
|
||||
|
||||
-- ==================================================================
|
||||
-- DATABASE CHANGES APPLIED ON 2026-04-22
|
||||
-- Migration: Calendar Module + Missing Column Fixes
|
||||
-- Applied by: AI Agent
|
||||
-- ==================================================================
|
||||
|
||||
-- ==================================================================
|
||||
-- 1. EVENTS TABLE (Calendar Module)
|
||||
-- Migration: 20260422021353_AddEventsTable
|
||||
-- ==================================================================
|
||||
|
||||
USE [db_a7141e_seahavenindustry]
|
||||
GO
|
||||
|
||||
IF NOT EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'[dbo].[Events]') AND type in (N'U'))
|
||||
BEGIN
|
||||
CREATE TABLE [dbo].[Events](
|
||||
[Id] [int] IDENTITY(1,1) NOT NULL,
|
||||
|
||||
-- Basic Event Information
|
||||
[Title] [nvarchar](100) NOT NULL,
|
||||
[Description] [nvarchar](max) NULL,
|
||||
[Location] [nvarchar](150) NULL,
|
||||
|
||||
-- Related Record (Link to Accounts, Contacts, Work Orders, etc.)
|
||||
[RecordID] [int] NULL,
|
||||
[RecordName] [nvarchar](max) NULL,
|
||||
[RecordTargetType] [nvarchar](max) NULL,
|
||||
|
||||
-- Reminder & Display Settings
|
||||
[ReminderMinutes] [int] NOT NULL CONSTRAINT [DF_Events_ReminderMinutes] DEFAULT ((15)),
|
||||
[EventColor] [nvarchar](20) NOT NULL CONSTRAINT [DF_Events_EventColor] DEFAULT (N'primary'),
|
||||
|
||||
-- Schedule Information
|
||||
[StartDate] [datetime2](7) NOT NULL,
|
||||
[StartTime] [nvarchar](10) NULL,
|
||||
[EndDate] [datetime2](7) NOT NULL,
|
||||
[EndTime] [nvarchar](10) NULL,
|
||||
[AllDay] [bit] NOT NULL CONSTRAINT [DF_Events_AllDay] DEFAULT ((0)),
|
||||
|
||||
-- Audit Fields (from FullAuditEntity)
|
||||
[IsDeleted] [bit] NOT NULL CONSTRAINT [DF_Events_IsDeleted] DEFAULT ((0)),
|
||||
[DeletionTime] [datetime2](7) NULL,
|
||||
[createdby] [nvarchar](max) NULL,
|
||||
[CreatedDate] [datetime2](7) NULL,
|
||||
[lastmodifiedby] [nvarchar](max) NULL,
|
||||
[LastModificationTime] [datetime2](7) NULL,
|
||||
|
||||
CONSTRAINT [PK_Events] PRIMARY KEY CLUSTERED ([Id] ASC)
|
||||
WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON)
|
||||
) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY]
|
||||
END
|
||||
GO
|
||||
|
||||
-- Create Index on StartDate for faster calendar queries
|
||||
IF NOT EXISTS (SELECT * FROM sys.indexes WHERE name = 'IX_Events_StartDate' AND object_id = OBJECT_ID('[dbo].[Events]'))
|
||||
BEGIN
|
||||
CREATE NONCLUSTERED INDEX [IX_Events_StartDate] ON [dbo].[Events]
|
||||
(
|
||||
[StartDate] ASC
|
||||
)
|
||||
WHERE ([IsDeleted] = 0)
|
||||
END
|
||||
GO
|
||||
|
||||
-- Create Index on IsDeleted for soft delete queries
|
||||
IF NOT EXISTS (SELECT * FROM sys.indexes WHERE name = 'IX_Events_IsDeleted' AND object_id = OBJECT_ID('[dbo].[Events]'))
|
||||
BEGIN
|
||||
CREATE NONCLUSTERED INDEX [IX_Events_IsDeleted] ON [dbo].[Events]
|
||||
(
|
||||
[IsDeleted] ASC
|
||||
)
|
||||
END
|
||||
GO
|
||||
|
||||
-- ==================================================================
|
||||
-- 2. WORKORDERS TABLE - MISSING COLUMNS
|
||||
-- Migrations: 20260416212330, 20260416224015, 20260417142453
|
||||
-- ==================================================================
|
||||
|
||||
-- Migration: 20260416224015_AddInternalWONumber
|
||||
ALTER TABLE [dbo].[WorkOrders]
|
||||
ADD [InternalWONumber] [nvarchar](max) NULL
|
||||
GO
|
||||
|
||||
-- Migration: 20260416212330_AddExternalSyncFields
|
||||
ALTER TABLE [dbo].[WorkOrders]
|
||||
ADD [ExternalWorkOrderId] [nvarchar](max) NULL,
|
||||
[Source] [nvarchar](max) NULL
|
||||
GO
|
||||
|
||||
-- Migration: 20260417142453_AddDropdownOptionsAndWOFields
|
||||
ALTER TABLE [dbo].[WorkOrders]
|
||||
ADD [Problem] [nvarchar](max) NULL,
|
||||
[Trade] [nvarchar](max) NULL,
|
||||
[SubTrade] [nvarchar](max) NULL,
|
||||
[VendorNTE] [decimal](18, 2) NULL,
|
||||
[ScheduledDate] [datetime2](7) NULL,
|
||||
[CompletedDate] [datetime2](7) NULL
|
||||
GO
|
||||
|
||||
-- ==================================================================
|
||||
-- 3. COMMENTS TABLE - MISSING COLUMNS
|
||||
-- Migrations: 20260416220656, 20260417171152
|
||||
-- ==================================================================
|
||||
|
||||
-- Migration: 20260416220656_AddCommentTypeAndAuditLog
|
||||
ALTER TABLE [dbo].[Comments]
|
||||
ADD [CommentType] [nvarchar](max) NULL,
|
||||
[Commenter] [nvarchar](max) NULL,
|
||||
[RecordType] [nvarchar](max) NULL
|
||||
GO
|
||||
|
||||
-- Migration: 20260417171152_AddDispatchNumberAndCommentDispatchId
|
||||
ALTER TABLE [dbo].[Comments]
|
||||
ADD [DispatchId] [int] NULL
|
||||
GO
|
||||
|
||||
-- ==================================================================
|
||||
-- 4. LOCATIONS TABLE - MISSING COLUMNS
|
||||
-- Migration: 20260416210833_AddLocationFields
|
||||
-- ==================================================================
|
||||
|
||||
ALTER TABLE [dbo].[Locations]
|
||||
ADD [Address1] [nvarchar](max) NULL,
|
||||
[Address2] [nvarchar](max) NULL,
|
||||
[City] [nvarchar](max) NULL,
|
||||
[State] [nvarchar](max) NULL,
|
||||
[Zip] [nvarchar](max) NULL,
|
||||
[PhoneNumber] [nvarchar](max) NULL,
|
||||
[Email] [nvarchar](max) NULL,
|
||||
[Status] [nvarchar](max) NULL
|
||||
GO
|
||||
|
||||
-- ==================================================================
|
||||
-- 5. MIGRATION HISTORY RECORDS
|
||||
-- Marked these migrations as applied to sync with code
|
||||
-- ==================================================================
|
||||
|
||||
INSERT INTO [dbo].[__EFMigrationsHistory] ([MigrationId], [ProductVersion])
|
||||
VALUES
|
||||
(N'20260416204109_SyncContactsAndModels', N'8.0.8'),
|
||||
(N'20260416210833_AddLocationFields', N'8.0.8'),
|
||||
(N'20260416212330_AddExternalSyncFields', N'8.0.8'),
|
||||
(N'20260416220656_AddCommentTypeAndAuditLog', N'8.0.8'),
|
||||
(N'20260416224015_AddInternalWONumber', N'8.0.8'),
|
||||
(N'20260417142453_AddDropdownOptionsAndWOFields', N'8.0.8'),
|
||||
(N'20260417153405_AddVendorAndDispatch', N'8.0.8'),
|
||||
(N'20260417154641_AddDispatchScheduledDate', N'8.0.8'),
|
||||
(N'20260417171152_AddDispatchNumberAndCommentDispatchId', N'8.0.8'),
|
||||
(N'20260417182910_AddTaskListTemplates', N'8.0.8'),
|
||||
(N'20260417190743_MultiWODispatch', N'8.0.8'),
|
||||
(N'20260417192012_AddDispatchChecklist', N'8.0.8'),
|
||||
(N'20260417194438_AddDispatchSignoffs', N'8.0.8'),
|
||||
(N'20260417195316_AddDispatchVerification', N'8.0.8'),
|
||||
(N'20260420151445_AddVendorAccessToken', N'8.0.8'),
|
||||
(N'20260420164708_AddDispatchUpliftRequest', N'8.0.8'),
|
||||
(N'20260422021353_AddEventsTable', N'8.0.8')
|
||||
GO
|
||||
|
||||
-- ==================================================================
|
||||
-- SUMMARY OF CHANGES (2026-04-22)
|
||||
-- ==================================================================
|
||||
-- Tables Created (11):
|
||||
-- 1. Events (Calendar/Events module)
|
||||
-- 2. WorkOrderAuditLogs (Audit trail for work order changes)
|
||||
-- 3. DropdownOptions (Dynamic dropdown values)
|
||||
-- 4. Vendors (Vendor management)
|
||||
-- 5. Dispatches (Vendor dispatch workflow)
|
||||
-- 6. TaskListTemplates (Reusable task templates)
|
||||
-- 7. TaskListTemplateItems (Template task items)
|
||||
-- 8. DispatchWorkOrders (Many-to-many: Dispatch <-> WorkOrders)
|
||||
-- 9. DispatchChecklistItems (Dispatch checklist)
|
||||
-- 10. DispatchSignoffs (Customer & Vendor signatures)
|
||||
-- 11. DispatchUpliftRequests (NTE increase requests)
|
||||
--
|
||||
-- Columns Added to WorkOrders (9):
|
||||
-- - InternalWONumber, ExternalWorkOrderId, Source
|
||||
-- - Problem, Trade, SubTrade
|
||||
-- - VendorNTE, ScheduledDate, CompletedDate
|
||||
--
|
||||
-- Columns Added to Comments (4):
|
||||
-- - CommentType, Commenter, RecordType, DispatchId
|
||||
--
|
||||
-- Columns Added to Locations (8):
|
||||
-- - Address1, Address2, City, State, Zip
|
||||
-- - PhoneNumber, Email, Status
|
||||
--
|
||||
-- Columns Added to Dispatches (7):
|
||||
-- - ScheduledDate, DispatchNumber, CompletedDate
|
||||
-- - VerifiedBy, VerifiedAt, AcceptToken, AcknowledgedAt
|
||||
--
|
||||
-- Columns Added to Vendors (1):
|
||||
-- - AccessToken
|
||||
--
|
||||
-- Migrations Applied: 17
|
||||
-- From: 20260416204109_SyncContactsAndModels
|
||||
-- To: 20260422021353_AddEventsTable
|
||||
--
|
||||
-- Database Status: UP TO DATE ✓
|
||||
-- ==================================================================
|
||||
|
||||
|
||||
|
|
|
|||
33145
Api.SeaHavenIndustries/wwwroot/zipcodes.csv
Normal file
33145
Api.SeaHavenIndustries/wwwroot/zipcodes.csv
Normal file
File diff suppressed because it is too large
Load diff
|
|
@ -24,6 +24,15 @@ namespace Data.SeaHavenIndustries
|
|||
{
|
||||
foreignKey.DeleteBehavior = DeleteBehavior.Restrict;
|
||||
}
|
||||
|
||||
builder.Entity<VendorAccessToken>()
|
||||
.HasIndex(t => t.Token)
|
||||
.IsUnique();
|
||||
|
||||
builder.Entity<DispatchUpliftRequest>()
|
||||
.HasIndex(u => new { u.DispatchId, u.Status })
|
||||
.IsUnique()
|
||||
.HasFilter("[Status] = 'Pending'");
|
||||
}
|
||||
public DbSet<Category> Categories { get; set; }
|
||||
public DbSet<Locations> Locations { get; set; }
|
||||
|
|
@ -37,6 +46,17 @@ namespace Data.SeaHavenIndustries
|
|||
public DbSet<WorkOrderContacts> WorkOrderContacts { get; set; }
|
||||
public DbSet<WorkOrderCategories> workOrderCategories { get; set; }
|
||||
public DbSet<WorkOrderAttachments> workOrderAttachments { get; set; }
|
||||
public DbSet<WorkOrderAuditLog> WorkOrderAuditLogs { get; set; }
|
||||
public DbSet<DropdownOption> DropdownOptions { get; set; }
|
||||
public DbSet<Vendor> Vendors { get; set; }
|
||||
public DbSet<VendorAccessToken> VendorAccessTokens { get; set; }
|
||||
public DbSet<Dispatch> Dispatches { get; set; }
|
||||
public DbSet<DispatchWorkOrder> DispatchWorkOrders { get; set; }
|
||||
public DbSet<DispatchChecklistItem> DispatchChecklistItems { get; set; }
|
||||
public DbSet<DispatchSignoff> DispatchSignoffs { get; set; }
|
||||
public DbSet<DispatchUpliftRequest> DispatchUpliftRequests { get; set; }
|
||||
public DbSet<TaskListTemplate> TaskListTemplates { get; set; }
|
||||
public DbSet<TaskListTemplateItem> TaskListTemplateItems { get; set; }
|
||||
|
||||
// New DbSets for added entities
|
||||
public DbSet<Accounts> Accounts { get; set; }
|
||||
|
|
|
|||
2138
Data.SeaHavenIndustries/Migrations/20260416204109_SyncContactsAndModels.Designer.cs
generated
Normal file
2138
Data.SeaHavenIndustries/Migrations/20260416204109_SyncContactsAndModels.Designer.cs
generated
Normal file
File diff suppressed because it is too large
Load diff
|
|
@ -0,0 +1,852 @@
|
|||
using System;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace Data.SeaHavenIndustries.Migrations
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public partial class SyncContactsAndModels : Migration
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.RenameColumn(
|
||||
name: "Name",
|
||||
table: "Contacts",
|
||||
newName: "Zip");
|
||||
|
||||
migrationBuilder.AddColumn<int>(
|
||||
name: "AccountId",
|
||||
table: "Contacts",
|
||||
type: "int",
|
||||
nullable: true);
|
||||
|
||||
migrationBuilder.AddColumn<string>(
|
||||
name: "Address1",
|
||||
table: "Contacts",
|
||||
type: "nvarchar(max)",
|
||||
nullable: true);
|
||||
|
||||
migrationBuilder.AddColumn<string>(
|
||||
name: "Address2",
|
||||
table: "Contacts",
|
||||
type: "nvarchar(max)",
|
||||
nullable: true);
|
||||
|
||||
migrationBuilder.AddColumn<string>(
|
||||
name: "City",
|
||||
table: "Contacts",
|
||||
type: "nvarchar(max)",
|
||||
nullable: true);
|
||||
|
||||
migrationBuilder.AddColumn<string>(
|
||||
name: "ContactType",
|
||||
table: "Contacts",
|
||||
type: "nvarchar(max)",
|
||||
nullable: true);
|
||||
|
||||
migrationBuilder.AddColumn<string>(
|
||||
name: "FacebookUrl",
|
||||
table: "Contacts",
|
||||
type: "nvarchar(max)",
|
||||
nullable: true);
|
||||
|
||||
migrationBuilder.AddColumn<string>(
|
||||
name: "FirstName",
|
||||
table: "Contacts",
|
||||
type: "nvarchar(max)",
|
||||
nullable: true);
|
||||
|
||||
migrationBuilder.AddColumn<string>(
|
||||
name: "LastName",
|
||||
table: "Contacts",
|
||||
type: "nvarchar(max)",
|
||||
nullable: true);
|
||||
|
||||
migrationBuilder.AddColumn<string>(
|
||||
name: "LinkedInUrl",
|
||||
table: "Contacts",
|
||||
type: "nvarchar(max)",
|
||||
nullable: true);
|
||||
|
||||
migrationBuilder.AddColumn<int>(
|
||||
name: "LocationId",
|
||||
table: "Contacts",
|
||||
type: "int",
|
||||
nullable: true);
|
||||
|
||||
migrationBuilder.AddColumn<string>(
|
||||
name: "MiddleName",
|
||||
table: "Contacts",
|
||||
type: "nvarchar(max)",
|
||||
nullable: true);
|
||||
|
||||
migrationBuilder.AddColumn<string>(
|
||||
name: "Owner",
|
||||
table: "Contacts",
|
||||
type: "nvarchar(max)",
|
||||
nullable: true);
|
||||
|
||||
migrationBuilder.AddColumn<string>(
|
||||
name: "State",
|
||||
table: "Contacts",
|
||||
type: "nvarchar(max)",
|
||||
nullable: true);
|
||||
|
||||
migrationBuilder.AddColumn<string>(
|
||||
name: "Title",
|
||||
table: "Contacts",
|
||||
type: "nvarchar(max)",
|
||||
nullable: true);
|
||||
|
||||
migrationBuilder.AddColumn<string>(
|
||||
name: "TwitterUrl",
|
||||
table: "Contacts",
|
||||
type: "nvarchar(max)",
|
||||
nullable: true);
|
||||
|
||||
migrationBuilder.AlterColumn<string>(
|
||||
name: "Name",
|
||||
table: "AspNetUserTokens",
|
||||
type: "nvarchar(450)",
|
||||
nullable: false,
|
||||
oldClrType: typeof(string),
|
||||
oldType: "nvarchar(128)",
|
||||
oldMaxLength: 128);
|
||||
|
||||
migrationBuilder.AlterColumn<string>(
|
||||
name: "LoginProvider",
|
||||
table: "AspNetUserTokens",
|
||||
type: "nvarchar(450)",
|
||||
nullable: false,
|
||||
oldClrType: typeof(string),
|
||||
oldType: "nvarchar(128)",
|
||||
oldMaxLength: 128);
|
||||
|
||||
migrationBuilder.AlterColumn<string>(
|
||||
name: "ProviderKey",
|
||||
table: "AspNetUserLogins",
|
||||
type: "nvarchar(450)",
|
||||
nullable: false,
|
||||
oldClrType: typeof(string),
|
||||
oldType: "nvarchar(128)",
|
||||
oldMaxLength: 128);
|
||||
|
||||
migrationBuilder.AlterColumn<string>(
|
||||
name: "LoginProvider",
|
||||
table: "AspNetUserLogins",
|
||||
type: "nvarchar(450)",
|
||||
nullable: false,
|
||||
oldClrType: typeof(string),
|
||||
oldType: "nvarchar(128)",
|
||||
oldMaxLength: 128);
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
name: "Accounts",
|
||||
columns: table => new
|
||||
{
|
||||
Id = table.Column<int>(type: "int", nullable: false)
|
||||
.Annotation("SqlServer:Identity", "1, 1"),
|
||||
Name = table.Column<string>(type: "nvarchar(max)", nullable: true),
|
||||
Type = table.Column<string>(type: "nvarchar(max)", nullable: true),
|
||||
Importance = table.Column<int>(type: "int", nullable: true),
|
||||
TerritoryId = table.Column<int>(type: "int", nullable: true),
|
||||
OwnerEmployeeId = table.Column<int>(type: "int", nullable: true),
|
||||
PrivacySetting = table.Column<string>(type: "nvarchar(max)", nullable: true),
|
||||
About = table.Column<string>(type: "nvarchar(max)", nullable: true),
|
||||
Tags = table.Column<string>(type: "nvarchar(max)", nullable: true),
|
||||
Phone = table.Column<string>(type: "nvarchar(max)", nullable: true),
|
||||
Email = table.Column<string>(type: "nvarchar(max)", nullable: true),
|
||||
IndustryId = table.Column<int>(type: "int", nullable: true),
|
||||
TimeZone = table.Column<string>(type: "nvarchar(max)", nullable: true),
|
||||
Website = table.Column<string>(type: "nvarchar(max)", nullable: true),
|
||||
CurrencyCode = table.Column<string>(type: "nvarchar(max)", nullable: true),
|
||||
ServiceInstructions = table.Column<string>(type: "nvarchar(max)", nullable: true),
|
||||
FacebookUrl = table.Column<string>(type: "nvarchar(max)", nullable: true),
|
||||
LinkedInUrl = table.Column<string>(type: "nvarchar(max)", nullable: true),
|
||||
TwitterUrl = table.Column<string>(type: "nvarchar(max)", nullable: true),
|
||||
IsDeleted = table.Column<bool>(type: "bit", nullable: true),
|
||||
createdby = table.Column<string>(type: "nvarchar(max)", nullable: true),
|
||||
DeleterUserId = table.Column<string>(type: "nvarchar(max)", nullable: true),
|
||||
DeletionTime = table.Column<DateTime>(type: "datetime2", nullable: true),
|
||||
CreatedDate = table.Column<DateTime>(type: "datetime2", nullable: true),
|
||||
LastModificationTime = table.Column<DateTime>(type: "datetime2", nullable: true),
|
||||
LastModifierUserId = table.Column<int>(type: "int", nullable: true)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_Accounts", x => x.Id);
|
||||
});
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
name: "ContactDetails",
|
||||
columns: table => new
|
||||
{
|
||||
Id = table.Column<int>(type: "int", nullable: false)
|
||||
.Annotation("SqlServer:Identity", "1, 1"),
|
||||
ContactId = table.Column<int>(type: "int", nullable: false),
|
||||
Gender = table.Column<string>(type: "nvarchar(max)", nullable: true),
|
||||
DateOfBirth = table.Column<DateTime>(type: "datetime2", nullable: true),
|
||||
Territory = table.Column<string>(type: "nvarchar(max)", nullable: true),
|
||||
PrivacySetting = table.Column<string>(type: "nvarchar(max)", nullable: true),
|
||||
About = table.Column<string>(type: "nvarchar(max)", nullable: true),
|
||||
Tags = table.Column<string>(type: "nvarchar(max)", nullable: true),
|
||||
FacebookUrl = table.Column<string>(type: "nvarchar(max)", nullable: true),
|
||||
LinkedInUrl = table.Column<string>(type: "nvarchar(max)", nullable: true),
|
||||
TwitterUrl = table.Column<string>(type: "nvarchar(max)", nullable: true),
|
||||
IsDeleted = table.Column<bool>(type: "bit", nullable: true),
|
||||
createdby = table.Column<string>(type: "nvarchar(max)", nullable: true),
|
||||
DeleterUserId = table.Column<string>(type: "nvarchar(max)", nullable: true),
|
||||
DeletionTime = table.Column<DateTime>(type: "datetime2", nullable: true),
|
||||
CreatedDate = table.Column<DateTime>(type: "datetime2", nullable: true),
|
||||
LastModificationTime = table.Column<DateTime>(type: "datetime2", nullable: true),
|
||||
LastModifierUserId = table.Column<int>(type: "int", nullable: true)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_ContactDetails", x => x.Id);
|
||||
table.ForeignKey(
|
||||
name: "FK_ContactDetails_Contacts_ContactId",
|
||||
column: x => x.ContactId,
|
||||
principalTable: "Contacts",
|
||||
principalColumn: "Id",
|
||||
onDelete: ReferentialAction.Restrict);
|
||||
});
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
name: "Departments",
|
||||
columns: table => new
|
||||
{
|
||||
Id = table.Column<int>(type: "int", nullable: false)
|
||||
.Annotation("SqlServer:Identity", "1, 1"),
|
||||
Name = table.Column<string>(type: "nvarchar(200)", maxLength: 200, nullable: false),
|
||||
IsDeleted = table.Column<bool>(type: "bit", nullable: true),
|
||||
createdby = table.Column<string>(type: "nvarchar(max)", nullable: true),
|
||||
DeleterUserId = table.Column<string>(type: "nvarchar(max)", nullable: true),
|
||||
DeletionTime = table.Column<DateTime>(type: "datetime2", nullable: true),
|
||||
CreatedDate = table.Column<DateTime>(type: "datetime2", nullable: true),
|
||||
LastModificationTime = table.Column<DateTime>(type: "datetime2", nullable: true),
|
||||
LastModifierUserId = table.Column<int>(type: "int", nullable: true)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_Departments", x => x.Id);
|
||||
});
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
name: "JobTitles",
|
||||
columns: table => new
|
||||
{
|
||||
Id = table.Column<int>(type: "int", nullable: false)
|
||||
.Annotation("SqlServer:Identity", "1, 1"),
|
||||
Name = table.Column<string>(type: "nvarchar(200)", maxLength: 200, nullable: false),
|
||||
IsDeleted = table.Column<bool>(type: "bit", nullable: true),
|
||||
createdby = table.Column<string>(type: "nvarchar(max)", nullable: true),
|
||||
DeleterUserId = table.Column<string>(type: "nvarchar(max)", nullable: true),
|
||||
DeletionTime = table.Column<DateTime>(type: "datetime2", nullable: true),
|
||||
CreatedDate = table.Column<DateTime>(type: "datetime2", nullable: true),
|
||||
LastModificationTime = table.Column<DateTime>(type: "datetime2", nullable: true),
|
||||
LastModifierUserId = table.Column<int>(type: "int", nullable: true)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_JobTitles", x => x.Id);
|
||||
});
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
name: "Regions",
|
||||
columns: table => new
|
||||
{
|
||||
Id = table.Column<int>(type: "int", nullable: false)
|
||||
.Annotation("SqlServer:Identity", "1, 1"),
|
||||
Name = table.Column<string>(type: "nvarchar(200)", maxLength: 200, nullable: false),
|
||||
IsDeleted = table.Column<bool>(type: "bit", nullable: true),
|
||||
createdby = table.Column<string>(type: "nvarchar(max)", nullable: true),
|
||||
DeleterUserId = table.Column<string>(type: "nvarchar(max)", nullable: true),
|
||||
DeletionTime = table.Column<DateTime>(type: "datetime2", nullable: true),
|
||||
CreatedDate = table.Column<DateTime>(type: "datetime2", nullable: true),
|
||||
LastModificationTime = table.Column<DateTime>(type: "datetime2", nullable: true),
|
||||
LastModifierUserId = table.Column<int>(type: "int", nullable: true)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_Regions", x => x.Id);
|
||||
});
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
name: "Addresses",
|
||||
columns: table => new
|
||||
{
|
||||
Id = table.Column<int>(type: "int", nullable: false)
|
||||
.Annotation("SqlServer:Identity", "1, 1"),
|
||||
AddressType = table.Column<string>(type: "nvarchar(max)", nullable: true),
|
||||
Address1 = table.Column<string>(type: "nvarchar(max)", nullable: true),
|
||||
Address2 = table.Column<string>(type: "nvarchar(max)", nullable: true),
|
||||
City = table.Column<string>(type: "nvarchar(max)", nullable: true),
|
||||
State = table.Column<string>(type: "nvarchar(max)", nullable: true),
|
||||
Zip = table.Column<string>(type: "nvarchar(max)", nullable: true),
|
||||
Country = table.Column<string>(type: "nvarchar(max)", nullable: true),
|
||||
County = table.Column<string>(type: "nvarchar(max)", nullable: true),
|
||||
AccountId = table.Column<int>(type: "int", nullable: true),
|
||||
ContactId = table.Column<int>(type: "int", nullable: true),
|
||||
IsDeleted = table.Column<bool>(type: "bit", nullable: true),
|
||||
createdby = table.Column<string>(type: "nvarchar(max)", nullable: true),
|
||||
DeleterUserId = table.Column<string>(type: "nvarchar(max)", nullable: true),
|
||||
DeletionTime = table.Column<DateTime>(type: "datetime2", nullable: true),
|
||||
CreatedDate = table.Column<DateTime>(type: "datetime2", nullable: true),
|
||||
LastModificationTime = table.Column<DateTime>(type: "datetime2", nullable: true),
|
||||
LastModifierUserId = table.Column<int>(type: "int", nullable: true)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_Addresses", x => x.Id);
|
||||
table.ForeignKey(
|
||||
name: "FK_Addresses_Accounts_AccountId",
|
||||
column: x => x.AccountId,
|
||||
principalTable: "Accounts",
|
||||
principalColumn: "Id",
|
||||
onDelete: ReferentialAction.Restrict);
|
||||
table.ForeignKey(
|
||||
name: "FK_Addresses_Contacts_ContactId",
|
||||
column: x => x.ContactId,
|
||||
principalTable: "Contacts",
|
||||
principalColumn: "Id",
|
||||
onDelete: ReferentialAction.Restrict);
|
||||
});
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
name: "Assets",
|
||||
columns: table => new
|
||||
{
|
||||
Id = table.Column<int>(type: "int", nullable: false)
|
||||
.Annotation("SqlServer:Identity", "1, 1"),
|
||||
AssetCode = table.Column<string>(type: "nvarchar(max)", nullable: false),
|
||||
Name = table.Column<string>(type: "nvarchar(max)", nullable: false),
|
||||
Description = table.Column<string>(type: "nvarchar(max)", nullable: true),
|
||||
AccountId = table.Column<int>(type: "int", nullable: true),
|
||||
LocationId = table.Column<int>(type: "int", nullable: true),
|
||||
IsActive = table.Column<bool>(type: "bit", nullable: false),
|
||||
IsDeleted = table.Column<bool>(type: "bit", nullable: true),
|
||||
createdby = table.Column<string>(type: "nvarchar(max)", nullable: true),
|
||||
DeleterUserId = table.Column<string>(type: "nvarchar(max)", nullable: true),
|
||||
DeletionTime = table.Column<DateTime>(type: "datetime2", nullable: true),
|
||||
CreatedDate = table.Column<DateTime>(type: "datetime2", nullable: true),
|
||||
LastModificationTime = table.Column<DateTime>(type: "datetime2", nullable: true),
|
||||
LastModifierUserId = table.Column<int>(type: "int", nullable: true)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_Assets", x => x.Id);
|
||||
table.ForeignKey(
|
||||
name: "FK_Assets_Accounts_AccountId",
|
||||
column: x => x.AccountId,
|
||||
principalTable: "Accounts",
|
||||
principalColumn: "Id",
|
||||
onDelete: ReferentialAction.Restrict);
|
||||
table.ForeignKey(
|
||||
name: "FK_Assets_Locations_LocationId",
|
||||
column: x => x.LocationId,
|
||||
principalTable: "Locations",
|
||||
principalColumn: "Id",
|
||||
onDelete: ReferentialAction.Restrict);
|
||||
});
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
name: "Employees",
|
||||
columns: table => new
|
||||
{
|
||||
Id = table.Column<int>(type: "int", nullable: false)
|
||||
.Annotation("SqlServer:Identity", "1, 1"),
|
||||
FirstName = table.Column<string>(type: "nvarchar(max)", nullable: false),
|
||||
MiddleName = table.Column<string>(type: "nvarchar(max)", nullable: true),
|
||||
LastName = table.Column<string>(type: "nvarchar(max)", nullable: false),
|
||||
DepartmentId = table.Column<int>(type: "int", nullable: true),
|
||||
TimeZone = table.Column<string>(type: "nvarchar(max)", nullable: true),
|
||||
Gender = table.Column<int>(type: "int", nullable: true),
|
||||
DateOfBirth = table.Column<DateTime>(type: "datetime2", nullable: true),
|
||||
JobTitleId = table.Column<int>(type: "int", nullable: true),
|
||||
EmployeeNumber = table.Column<string>(type: "nvarchar(max)", nullable: true),
|
||||
HireDate = table.Column<DateTime>(type: "datetime2", nullable: true),
|
||||
RegionId = table.Column<int>(type: "int", nullable: true),
|
||||
CanChangeProviderNumber = table.Column<bool>(type: "bit", nullable: true),
|
||||
About = table.Column<string>(type: "nvarchar(max)", nullable: true),
|
||||
PermissionGroupId = table.Column<int>(type: "int", nullable: true),
|
||||
UserName = table.Column<string>(type: "nvarchar(max)", nullable: true),
|
||||
ConfirmUserName = table.Column<string>(type: "nvarchar(max)", nullable: true),
|
||||
SMSAddress = table.Column<string>(type: "nvarchar(max)", nullable: true),
|
||||
Website = table.Column<string>(type: "nvarchar(max)", nullable: true),
|
||||
FacebookUrl = table.Column<string>(type: "nvarchar(max)", nullable: true),
|
||||
LinkedInUrl = table.Column<string>(type: "nvarchar(max)", nullable: true),
|
||||
TwitterUrl = table.Column<string>(type: "nvarchar(max)", nullable: true),
|
||||
Status = table.Column<string>(type: "nvarchar(max)", nullable: true),
|
||||
LastLogin = table.Column<DateTime>(type: "datetime2", nullable: true),
|
||||
IsDeleted = table.Column<bool>(type: "bit", nullable: true),
|
||||
CreatedBy = table.Column<string>(type: "nvarchar(max)", nullable: true),
|
||||
CreatedDate = table.Column<DateTime>(type: "datetime2", nullable: true),
|
||||
LastModificationTime = table.Column<DateTime>(type: "datetime2", nullable: true),
|
||||
LastModifierUserId = table.Column<int>(type: "int", nullable: true)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_Employees", x => x.Id);
|
||||
table.ForeignKey(
|
||||
name: "FK_Employees_Departments_DepartmentId",
|
||||
column: x => x.DepartmentId,
|
||||
principalTable: "Departments",
|
||||
principalColumn: "Id",
|
||||
onDelete: ReferentialAction.Restrict);
|
||||
table.ForeignKey(
|
||||
name: "FK_Employees_JobTitles_JobTitleId",
|
||||
column: x => x.JobTitleId,
|
||||
principalTable: "JobTitles",
|
||||
principalColumn: "Id",
|
||||
onDelete: ReferentialAction.Restrict);
|
||||
table.ForeignKey(
|
||||
name: "FK_Employees_Regions_RegionId",
|
||||
column: x => x.RegionId,
|
||||
principalTable: "Regions",
|
||||
principalColumn: "Id",
|
||||
onDelete: ReferentialAction.Restrict);
|
||||
});
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
name: "PMSchedules",
|
||||
columns: table => new
|
||||
{
|
||||
Id = table.Column<int>(type: "int", nullable: false)
|
||||
.Annotation("SqlServer:Identity", "1, 1"),
|
||||
Name = table.Column<string>(type: "nvarchar(max)", nullable: false),
|
||||
Frequency = table.Column<string>(type: "nvarchar(max)", nullable: false),
|
||||
StartDate = table.Column<DateTime>(type: "datetime2", nullable: false),
|
||||
IsActive = table.Column<bool>(type: "bit", nullable: false),
|
||||
AssetId = table.Column<int>(type: "int", nullable: true),
|
||||
LocationId = table.Column<int>(type: "int", nullable: true),
|
||||
IsDeleted = table.Column<bool>(type: "bit", nullable: true),
|
||||
createdby = table.Column<string>(type: "nvarchar(max)", nullable: true),
|
||||
DeleterUserId = table.Column<string>(type: "nvarchar(max)", nullable: true),
|
||||
DeletionTime = table.Column<DateTime>(type: "datetime2", nullable: true),
|
||||
CreatedDate = table.Column<DateTime>(type: "datetime2", nullable: true),
|
||||
LastModificationTime = table.Column<DateTime>(type: "datetime2", nullable: true),
|
||||
LastModifierUserId = table.Column<int>(type: "int", nullable: true)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_PMSchedules", x => x.Id);
|
||||
table.ForeignKey(
|
||||
name: "FK_PMSchedules_Assets_AssetId",
|
||||
column: x => x.AssetId,
|
||||
principalTable: "Assets",
|
||||
principalColumn: "Id",
|
||||
onDelete: ReferentialAction.Restrict);
|
||||
table.ForeignKey(
|
||||
name: "FK_PMSchedules_Locations_LocationId",
|
||||
column: x => x.LocationId,
|
||||
principalTable: "Locations",
|
||||
principalColumn: "Id",
|
||||
onDelete: ReferentialAction.Restrict);
|
||||
});
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
name: "EmployeeAddresses",
|
||||
columns: table => new
|
||||
{
|
||||
Id = table.Column<int>(type: "int", nullable: false)
|
||||
.Annotation("SqlServer:Identity", "1, 1"),
|
||||
EmployeeId = table.Column<int>(type: "int", nullable: false),
|
||||
Street = table.Column<string>(type: "nvarchar(max)", nullable: true),
|
||||
StreetLineTwo = table.Column<string>(type: "nvarchar(max)", nullable: true),
|
||||
City = table.Column<string>(type: "nvarchar(max)", nullable: true),
|
||||
Zip = table.Column<string>(type: "nvarchar(max)", nullable: true),
|
||||
CountryCode = table.Column<string>(type: "nvarchar(max)", nullable: true),
|
||||
StateCode = table.Column<string>(type: "nvarchar(max)", nullable: true),
|
||||
CountyId = table.Column<int>(type: "int", nullable: true)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_EmployeeAddresses", x => x.Id);
|
||||
table.ForeignKey(
|
||||
name: "FK_EmployeeAddresses_Employees_EmployeeId",
|
||||
column: x => x.EmployeeId,
|
||||
principalTable: "Employees",
|
||||
principalColumn: "Id",
|
||||
onDelete: ReferentialAction.Restrict);
|
||||
});
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
name: "EmployeeEmails",
|
||||
columns: table => new
|
||||
{
|
||||
Id = table.Column<int>(type: "int", nullable: false)
|
||||
.Annotation("SqlServer:Identity", "1, 1"),
|
||||
EmployeeId = table.Column<int>(type: "int", nullable: false),
|
||||
EmailTypeId = table.Column<int>(type: "int", nullable: true),
|
||||
Address = table.Column<string>(type: "nvarchar(256)", maxLength: 256, nullable: true),
|
||||
IsPrimary = table.Column<bool>(type: "bit", nullable: true),
|
||||
IsDeleted = table.Column<bool>(type: "bit", nullable: true),
|
||||
createdby = table.Column<string>(type: "nvarchar(max)", nullable: true),
|
||||
DeleterUserId = table.Column<string>(type: "nvarchar(max)", nullable: true),
|
||||
DeletionTime = table.Column<DateTime>(type: "datetime2", nullable: true),
|
||||
CreatedDate = table.Column<DateTime>(type: "datetime2", nullable: true),
|
||||
LastModificationTime = table.Column<DateTime>(type: "datetime2", nullable: true),
|
||||
LastModifierUserId = table.Column<int>(type: "int", nullable: true)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_EmployeeEmails", x => x.Id);
|
||||
table.ForeignKey(
|
||||
name: "FK_EmployeeEmails_Employees_EmployeeId",
|
||||
column: x => x.EmployeeId,
|
||||
principalTable: "Employees",
|
||||
principalColumn: "Id",
|
||||
onDelete: ReferentialAction.Restrict);
|
||||
});
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
name: "EmployeePhones",
|
||||
columns: table => new
|
||||
{
|
||||
Id = table.Column<int>(type: "int", nullable: false)
|
||||
.Annotation("SqlServer:Identity", "1, 1"),
|
||||
EmployeeId = table.Column<int>(type: "int", nullable: false),
|
||||
PhoneTypeId = table.Column<int>(type: "int", nullable: true),
|
||||
Number = table.Column<string>(type: "nvarchar(max)", nullable: true),
|
||||
Extension = table.Column<string>(type: "nvarchar(max)", nullable: true),
|
||||
IsPrimary = table.Column<bool>(type: "bit", nullable: true)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_EmployeePhones", x => x.Id);
|
||||
table.ForeignKey(
|
||||
name: "FK_EmployeePhones_Employees_EmployeeId",
|
||||
column: x => x.EmployeeId,
|
||||
principalTable: "Employees",
|
||||
principalColumn: "Id",
|
||||
onDelete: ReferentialAction.Restrict);
|
||||
});
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
name: "FollowUps",
|
||||
columns: table => new
|
||||
{
|
||||
Id = table.Column<int>(type: "int", nullable: false)
|
||||
.Annotation("SqlServer:Identity", "1, 1"),
|
||||
Reason = table.Column<string>(type: "nvarchar(max)", nullable: false),
|
||||
ScheduleDate = table.Column<DateTime>(type: "datetime2", nullable: false),
|
||||
ScheduleStartTime = table.Column<TimeSpan>(type: "time", nullable: false),
|
||||
Status = table.Column<string>(type: "nvarchar(max)", nullable: false),
|
||||
EmployeeId = table.Column<int>(type: "int", nullable: true),
|
||||
WorkOrderId = table.Column<int>(type: "int", nullable: true),
|
||||
AccountId = table.Column<int>(type: "int", nullable: true),
|
||||
LocationId = table.Column<int>(type: "int", nullable: true),
|
||||
IsDeleted = table.Column<bool>(type: "bit", nullable: true),
|
||||
createdby = table.Column<string>(type: "nvarchar(max)", nullable: true),
|
||||
DeleterUserId = table.Column<string>(type: "nvarchar(max)", nullable: true),
|
||||
DeletionTime = table.Column<DateTime>(type: "datetime2", nullable: true),
|
||||
CreatedDate = table.Column<DateTime>(type: "datetime2", nullable: true),
|
||||
LastModificationTime = table.Column<DateTime>(type: "datetime2", nullable: true),
|
||||
LastModifierUserId = table.Column<int>(type: "int", nullable: true)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_FollowUps", x => x.Id);
|
||||
table.ForeignKey(
|
||||
name: "FK_FollowUps_Accounts_AccountId",
|
||||
column: x => x.AccountId,
|
||||
principalTable: "Accounts",
|
||||
principalColumn: "Id",
|
||||
onDelete: ReferentialAction.Restrict);
|
||||
table.ForeignKey(
|
||||
name: "FK_FollowUps_Employees_EmployeeId",
|
||||
column: x => x.EmployeeId,
|
||||
principalTable: "Employees",
|
||||
principalColumn: "Id",
|
||||
onDelete: ReferentialAction.Restrict);
|
||||
table.ForeignKey(
|
||||
name: "FK_FollowUps_Locations_LocationId",
|
||||
column: x => x.LocationId,
|
||||
principalTable: "Locations",
|
||||
principalColumn: "Id",
|
||||
onDelete: ReferentialAction.Restrict);
|
||||
table.ForeignKey(
|
||||
name: "FK_FollowUps_workOrders_WorkOrderId",
|
||||
column: x => x.WorkOrderId,
|
||||
principalTable: "workOrders",
|
||||
principalColumn: "Id",
|
||||
onDelete: ReferentialAction.Restrict);
|
||||
});
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_Contacts_AccountId",
|
||||
table: "Contacts",
|
||||
column: "AccountId");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_Contacts_LocationId",
|
||||
table: "Contacts",
|
||||
column: "LocationId");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_Addresses_AccountId",
|
||||
table: "Addresses",
|
||||
column: "AccountId");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_Addresses_ContactId",
|
||||
table: "Addresses",
|
||||
column: "ContactId");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_Assets_AccountId",
|
||||
table: "Assets",
|
||||
column: "AccountId");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_Assets_LocationId",
|
||||
table: "Assets",
|
||||
column: "LocationId");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_ContactDetails_ContactId",
|
||||
table: "ContactDetails",
|
||||
column: "ContactId");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_EmployeeAddresses_EmployeeId",
|
||||
table: "EmployeeAddresses",
|
||||
column: "EmployeeId",
|
||||
unique: true);
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_EmployeeEmails_EmployeeId",
|
||||
table: "EmployeeEmails",
|
||||
column: "EmployeeId");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_EmployeePhones_EmployeeId",
|
||||
table: "EmployeePhones",
|
||||
column: "EmployeeId");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_Employees_DepartmentId",
|
||||
table: "Employees",
|
||||
column: "DepartmentId");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_Employees_JobTitleId",
|
||||
table: "Employees",
|
||||
column: "JobTitleId");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_Employees_RegionId",
|
||||
table: "Employees",
|
||||
column: "RegionId");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_FollowUps_AccountId",
|
||||
table: "FollowUps",
|
||||
column: "AccountId");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_FollowUps_EmployeeId",
|
||||
table: "FollowUps",
|
||||
column: "EmployeeId");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_FollowUps_LocationId",
|
||||
table: "FollowUps",
|
||||
column: "LocationId");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_FollowUps_WorkOrderId",
|
||||
table: "FollowUps",
|
||||
column: "WorkOrderId");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_PMSchedules_AssetId",
|
||||
table: "PMSchedules",
|
||||
column: "AssetId");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_PMSchedules_LocationId",
|
||||
table: "PMSchedules",
|
||||
column: "LocationId");
|
||||
|
||||
migrationBuilder.AddForeignKey(
|
||||
name: "FK_Contacts_Accounts_AccountId",
|
||||
table: "Contacts",
|
||||
column: "AccountId",
|
||||
principalTable: "Accounts",
|
||||
principalColumn: "Id",
|
||||
onDelete: ReferentialAction.Restrict);
|
||||
|
||||
migrationBuilder.AddForeignKey(
|
||||
name: "FK_Contacts_Locations_LocationId",
|
||||
table: "Contacts",
|
||||
column: "LocationId",
|
||||
principalTable: "Locations",
|
||||
principalColumn: "Id",
|
||||
onDelete: ReferentialAction.Restrict);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropForeignKey(
|
||||
name: "FK_Contacts_Accounts_AccountId",
|
||||
table: "Contacts");
|
||||
|
||||
migrationBuilder.DropForeignKey(
|
||||
name: "FK_Contacts_Locations_LocationId",
|
||||
table: "Contacts");
|
||||
|
||||
migrationBuilder.DropTable(
|
||||
name: "Addresses");
|
||||
|
||||
migrationBuilder.DropTable(
|
||||
name: "ContactDetails");
|
||||
|
||||
migrationBuilder.DropTable(
|
||||
name: "EmployeeAddresses");
|
||||
|
||||
migrationBuilder.DropTable(
|
||||
name: "EmployeeEmails");
|
||||
|
||||
migrationBuilder.DropTable(
|
||||
name: "EmployeePhones");
|
||||
|
||||
migrationBuilder.DropTable(
|
||||
name: "FollowUps");
|
||||
|
||||
migrationBuilder.DropTable(
|
||||
name: "PMSchedules");
|
||||
|
||||
migrationBuilder.DropTable(
|
||||
name: "Employees");
|
||||
|
||||
migrationBuilder.DropTable(
|
||||
name: "Assets");
|
||||
|
||||
migrationBuilder.DropTable(
|
||||
name: "Departments");
|
||||
|
||||
migrationBuilder.DropTable(
|
||||
name: "JobTitles");
|
||||
|
||||
migrationBuilder.DropTable(
|
||||
name: "Regions");
|
||||
|
||||
migrationBuilder.DropTable(
|
||||
name: "Accounts");
|
||||
|
||||
migrationBuilder.DropIndex(
|
||||
name: "IX_Contacts_AccountId",
|
||||
table: "Contacts");
|
||||
|
||||
migrationBuilder.DropIndex(
|
||||
name: "IX_Contacts_LocationId",
|
||||
table: "Contacts");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "AccountId",
|
||||
table: "Contacts");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "Address1",
|
||||
table: "Contacts");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "Address2",
|
||||
table: "Contacts");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "City",
|
||||
table: "Contacts");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "ContactType",
|
||||
table: "Contacts");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "FacebookUrl",
|
||||
table: "Contacts");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "FirstName",
|
||||
table: "Contacts");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "LastName",
|
||||
table: "Contacts");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "LinkedInUrl",
|
||||
table: "Contacts");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "LocationId",
|
||||
table: "Contacts");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "MiddleName",
|
||||
table: "Contacts");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "Owner",
|
||||
table: "Contacts");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "State",
|
||||
table: "Contacts");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "Title",
|
||||
table: "Contacts");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "TwitterUrl",
|
||||
table: "Contacts");
|
||||
|
||||
migrationBuilder.RenameColumn(
|
||||
name: "Zip",
|
||||
table: "Contacts",
|
||||
newName: "Name");
|
||||
|
||||
migrationBuilder.AlterColumn<string>(
|
||||
name: "Name",
|
||||
table: "AspNetUserTokens",
|
||||
type: "nvarchar(128)",
|
||||
maxLength: 128,
|
||||
nullable: false,
|
||||
oldClrType: typeof(string),
|
||||
oldType: "nvarchar(450)");
|
||||
|
||||
migrationBuilder.AlterColumn<string>(
|
||||
name: "LoginProvider",
|
||||
table: "AspNetUserTokens",
|
||||
type: "nvarchar(128)",
|
||||
maxLength: 128,
|
||||
nullable: false,
|
||||
oldClrType: typeof(string),
|
||||
oldType: "nvarchar(450)");
|
||||
|
||||
migrationBuilder.AlterColumn<string>(
|
||||
name: "ProviderKey",
|
||||
table: "AspNetUserLogins",
|
||||
type: "nvarchar(128)",
|
||||
maxLength: 128,
|
||||
nullable: false,
|
||||
oldClrType: typeof(string),
|
||||
oldType: "nvarchar(450)");
|
||||
|
||||
migrationBuilder.AlterColumn<string>(
|
||||
name: "LoginProvider",
|
||||
table: "AspNetUserLogins",
|
||||
type: "nvarchar(128)",
|
||||
maxLength: 128,
|
||||
nullable: false,
|
||||
oldClrType: typeof(string),
|
||||
oldType: "nvarchar(450)");
|
||||
}
|
||||
}
|
||||
}
|
||||
2177
Data.SeaHavenIndustries/Migrations/20260416210833_AddLocationFields.Designer.cs
generated
Normal file
2177
Data.SeaHavenIndustries/Migrations/20260416210833_AddLocationFields.Designer.cs
generated
Normal file
File diff suppressed because it is too large
Load diff
|
|
@ -0,0 +1,129 @@
|
|||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace Data.SeaHavenIndustries.Migrations
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public partial class AddLocationFields : Migration
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.AddColumn<int>(
|
||||
name: "AccountId",
|
||||
table: "Locations",
|
||||
type: "int",
|
||||
nullable: true);
|
||||
|
||||
migrationBuilder.AddColumn<string>(
|
||||
name: "Address",
|
||||
table: "Locations",
|
||||
type: "nvarchar(max)",
|
||||
nullable: true);
|
||||
|
||||
migrationBuilder.AddColumn<string>(
|
||||
name: "City",
|
||||
table: "Locations",
|
||||
type: "nvarchar(max)",
|
||||
nullable: true);
|
||||
|
||||
migrationBuilder.AddColumn<string>(
|
||||
name: "Contact",
|
||||
table: "Locations",
|
||||
type: "nvarchar(max)",
|
||||
nullable: true);
|
||||
|
||||
migrationBuilder.AddColumn<string>(
|
||||
name: "ContactEmail",
|
||||
table: "Locations",
|
||||
type: "nvarchar(max)",
|
||||
nullable: true);
|
||||
|
||||
migrationBuilder.AddColumn<string>(
|
||||
name: "Phone",
|
||||
table: "Locations",
|
||||
type: "nvarchar(max)",
|
||||
nullable: true);
|
||||
|
||||
migrationBuilder.AddColumn<string>(
|
||||
name: "State",
|
||||
table: "Locations",
|
||||
type: "nvarchar(max)",
|
||||
nullable: true);
|
||||
|
||||
migrationBuilder.AddColumn<string>(
|
||||
name: "Status",
|
||||
table: "Locations",
|
||||
type: "nvarchar(max)",
|
||||
nullable: true);
|
||||
|
||||
migrationBuilder.AddColumn<string>(
|
||||
name: "ZipCode",
|
||||
table: "Locations",
|
||||
type: "nvarchar(max)",
|
||||
nullable: true);
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_Locations_AccountId",
|
||||
table: "Locations",
|
||||
column: "AccountId");
|
||||
|
||||
migrationBuilder.AddForeignKey(
|
||||
name: "FK_Locations_Accounts_AccountId",
|
||||
table: "Locations",
|
||||
column: "AccountId",
|
||||
principalTable: "Accounts",
|
||||
principalColumn: "Id",
|
||||
onDelete: ReferentialAction.Restrict);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropForeignKey(
|
||||
name: "FK_Locations_Accounts_AccountId",
|
||||
table: "Locations");
|
||||
|
||||
migrationBuilder.DropIndex(
|
||||
name: "IX_Locations_AccountId",
|
||||
table: "Locations");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "AccountId",
|
||||
table: "Locations");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "Address",
|
||||
table: "Locations");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "City",
|
||||
table: "Locations");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "Contact",
|
||||
table: "Locations");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "ContactEmail",
|
||||
table: "Locations");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "Phone",
|
||||
table: "Locations");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "State",
|
||||
table: "Locations");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "Status",
|
||||
table: "Locations");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "ZipCode",
|
||||
table: "Locations");
|
||||
}
|
||||
}
|
||||
}
|
||||
2210
Data.SeaHavenIndustries/Migrations/20260416212330_AddExternalSyncFields.Designer.cs
generated
Normal file
2210
Data.SeaHavenIndustries/Migrations/20260416212330_AddExternalSyncFields.Designer.cs
generated
Normal file
File diff suppressed because it is too large
Load diff
|
|
@ -0,0 +1,129 @@
|
|||
using System;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace Data.SeaHavenIndustries.Migrations
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public partial class AddExternalSyncFields : Migration
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.AddColumn<string>(
|
||||
name: "Building",
|
||||
table: "workOrders",
|
||||
type: "nvarchar(max)",
|
||||
nullable: true);
|
||||
|
||||
migrationBuilder.AddColumn<string>(
|
||||
name: "Customer",
|
||||
table: "workOrders",
|
||||
type: "nvarchar(max)",
|
||||
nullable: true);
|
||||
|
||||
migrationBuilder.AddColumn<DateTime>(
|
||||
name: "DateReported",
|
||||
table: "workOrders",
|
||||
type: "datetime2",
|
||||
nullable: true);
|
||||
|
||||
migrationBuilder.AddColumn<string>(
|
||||
name: "ExternalWorkOrderId",
|
||||
table: "workOrders",
|
||||
type: "nvarchar(max)",
|
||||
nullable: true);
|
||||
|
||||
migrationBuilder.AddColumn<DateTime>(
|
||||
name: "ScheduledStart",
|
||||
table: "workOrders",
|
||||
type: "datetime2",
|
||||
nullable: true);
|
||||
|
||||
migrationBuilder.AddColumn<string>(
|
||||
name: "Severity",
|
||||
table: "workOrders",
|
||||
type: "nvarchar(max)",
|
||||
nullable: true);
|
||||
|
||||
migrationBuilder.AddColumn<string>(
|
||||
name: "SiteCode",
|
||||
table: "workOrders",
|
||||
type: "nvarchar(max)",
|
||||
nullable: true);
|
||||
|
||||
migrationBuilder.AddColumn<string>(
|
||||
name: "SourceEmailS3Key",
|
||||
table: "workOrders",
|
||||
type: "nvarchar(max)",
|
||||
nullable: true);
|
||||
|
||||
migrationBuilder.AddColumn<string>(
|
||||
name: "Commenter",
|
||||
table: "Comments",
|
||||
type: "nvarchar(max)",
|
||||
nullable: true);
|
||||
|
||||
migrationBuilder.AddColumn<string>(
|
||||
name: "ExternalCommentId",
|
||||
table: "Comments",
|
||||
type: "nvarchar(max)",
|
||||
nullable: true);
|
||||
|
||||
migrationBuilder.AddColumn<string>(
|
||||
name: "RecordType",
|
||||
table: "Comments",
|
||||
type: "nvarchar(max)",
|
||||
nullable: true);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropColumn(
|
||||
name: "Building",
|
||||
table: "workOrders");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "Customer",
|
||||
table: "workOrders");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "DateReported",
|
||||
table: "workOrders");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "ExternalWorkOrderId",
|
||||
table: "workOrders");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "ScheduledStart",
|
||||
table: "workOrders");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "Severity",
|
||||
table: "workOrders");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "SiteCode",
|
||||
table: "workOrders");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "SourceEmailS3Key",
|
||||
table: "workOrders");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "Commenter",
|
||||
table: "Comments");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "ExternalCommentId",
|
||||
table: "Comments");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "RecordType",
|
||||
table: "Comments");
|
||||
}
|
||||
}
|
||||
}
|
||||
2269
Data.SeaHavenIndustries/Migrations/20260416220656_AddCommentTypeAndAuditLog.Designer.cs
generated
Normal file
2269
Data.SeaHavenIndustries/Migrations/20260416220656_AddCommentTypeAndAuditLog.Designer.cs
generated
Normal file
File diff suppressed because it is too large
Load diff
|
|
@ -0,0 +1,73 @@
|
|||
using System;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace Data.SeaHavenIndustries.Migrations
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public partial class AddCommentTypeAndAuditLog : Migration
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.AddColumn<string>(
|
||||
name: "CommentType",
|
||||
table: "Comments",
|
||||
type: "nvarchar(max)",
|
||||
nullable: true);
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
name: "WorkOrderAuditLogs",
|
||||
columns: table => new
|
||||
{
|
||||
Id = table.Column<int>(type: "int", nullable: false)
|
||||
.Annotation("SqlServer:Identity", "1, 1"),
|
||||
WorkOrderId = table.Column<int>(type: "int", nullable: false),
|
||||
UserId = table.Column<string>(type: "nvarchar(450)", nullable: true),
|
||||
FieldName = table.Column<string>(type: "nvarchar(max)", nullable: true),
|
||||
OldValue = table.Column<string>(type: "nvarchar(max)", nullable: true),
|
||||
NewValue = table.Column<string>(type: "nvarchar(max)", nullable: true),
|
||||
Action = table.Column<string>(type: "nvarchar(max)", nullable: true),
|
||||
CreatedAt = table.Column<DateTime>(type: "datetime2", nullable: false)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_WorkOrderAuditLogs", x => x.Id);
|
||||
table.ForeignKey(
|
||||
name: "FK_WorkOrderAuditLogs_AspNetUsers_UserId",
|
||||
column: x => x.UserId,
|
||||
principalTable: "AspNetUsers",
|
||||
principalColumn: "Id",
|
||||
onDelete: ReferentialAction.Restrict);
|
||||
table.ForeignKey(
|
||||
name: "FK_WorkOrderAuditLogs_workOrders_WorkOrderId",
|
||||
column: x => x.WorkOrderId,
|
||||
principalTable: "workOrders",
|
||||
principalColumn: "Id",
|
||||
onDelete: ReferentialAction.Restrict);
|
||||
});
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_WorkOrderAuditLogs_UserId",
|
||||
table: "WorkOrderAuditLogs",
|
||||
column: "UserId");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_WorkOrderAuditLogs_WorkOrderId",
|
||||
table: "WorkOrderAuditLogs",
|
||||
column: "WorkOrderId");
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropTable(
|
||||
name: "WorkOrderAuditLogs");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "CommentType",
|
||||
table: "Comments");
|
||||
}
|
||||
}
|
||||
}
|
||||
2272
Data.SeaHavenIndustries/Migrations/20260416224015_AddInternalWONumber.Designer.cs
generated
Normal file
2272
Data.SeaHavenIndustries/Migrations/20260416224015_AddInternalWONumber.Designer.cs
generated
Normal file
File diff suppressed because it is too large
Load diff
|
|
@ -0,0 +1,28 @@
|
|||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace Data.SeaHavenIndustries.Migrations
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public partial class AddInternalWONumber : Migration
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.AddColumn<string>(
|
||||
name: "InternalWONumber",
|
||||
table: "workOrders",
|
||||
type: "nvarchar(max)",
|
||||
nullable: true);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropColumn(
|
||||
name: "InternalWONumber",
|
||||
table: "workOrders");
|
||||
}
|
||||
}
|
||||
}
|
||||
2323
Data.SeaHavenIndustries/Migrations/20260417142453_AddDropdownOptionsAndWOFields.Designer.cs
generated
Normal file
2323
Data.SeaHavenIndustries/Migrations/20260417142453_AddDropdownOptionsAndWOFields.Designer.cs
generated
Normal file
File diff suppressed because it is too large
Load diff
|
|
@ -0,0 +1,109 @@
|
|||
using System;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace Data.SeaHavenIndustries.Migrations
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public partial class AddDropdownOptionsAndWOFields : Migration
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.AddColumn<DateTime>(
|
||||
name: "CompletedDate",
|
||||
table: "workOrders",
|
||||
type: "datetime2",
|
||||
nullable: true);
|
||||
|
||||
migrationBuilder.AddColumn<string>(
|
||||
name: "Problem",
|
||||
table: "workOrders",
|
||||
type: "nvarchar(max)",
|
||||
nullable: true);
|
||||
|
||||
migrationBuilder.AddColumn<DateTime>(
|
||||
name: "ScheduledDate",
|
||||
table: "workOrders",
|
||||
type: "datetime2",
|
||||
nullable: true);
|
||||
|
||||
migrationBuilder.AddColumn<string>(
|
||||
name: "Source",
|
||||
table: "workOrders",
|
||||
type: "nvarchar(max)",
|
||||
nullable: true);
|
||||
|
||||
migrationBuilder.AddColumn<string>(
|
||||
name: "SubTrade",
|
||||
table: "workOrders",
|
||||
type: "nvarchar(max)",
|
||||
nullable: true);
|
||||
|
||||
migrationBuilder.AddColumn<string>(
|
||||
name: "Trade",
|
||||
table: "workOrders",
|
||||
type: "nvarchar(max)",
|
||||
nullable: true);
|
||||
|
||||
migrationBuilder.AddColumn<decimal>(
|
||||
name: "VendorNTE",
|
||||
table: "workOrders",
|
||||
type: "decimal(18,2)",
|
||||
nullable: true);
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
name: "DropdownOptions",
|
||||
columns: table => new
|
||||
{
|
||||
Id = table.Column<int>(type: "int", nullable: false)
|
||||
.Annotation("SqlServer:Identity", "1, 1"),
|
||||
Category = table.Column<string>(type: "nvarchar(max)", nullable: false),
|
||||
Value = table.Column<string>(type: "nvarchar(max)", nullable: false),
|
||||
ParentValue = table.Column<string>(type: "nvarchar(max)", nullable: true),
|
||||
SortOrder = table.Column<int>(type: "int", nullable: false),
|
||||
IsActive = table.Column<bool>(type: "bit", nullable: false)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_DropdownOptions", x => x.Id);
|
||||
});
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropTable(
|
||||
name: "DropdownOptions");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "CompletedDate",
|
||||
table: "workOrders");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "Problem",
|
||||
table: "workOrders");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "ScheduledDate",
|
||||
table: "workOrders");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "Source",
|
||||
table: "workOrders");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "SubTrade",
|
||||
table: "workOrders");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "Trade",
|
||||
table: "workOrders");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "VendorNTE",
|
||||
table: "workOrders");
|
||||
}
|
||||
}
|
||||
}
|
||||
2478
Data.SeaHavenIndustries/Migrations/20260417153405_AddVendorAndDispatch.Designer.cs
generated
Normal file
2478
Data.SeaHavenIndustries/Migrations/20260417153405_AddVendorAndDispatch.Designer.cs
generated
Normal file
File diff suppressed because it is too large
Load diff
|
|
@ -0,0 +1,104 @@
|
|||
using System;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace Data.SeaHavenIndustries.Migrations
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public partial class AddVendorAndDispatch : Migration
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.CreateTable(
|
||||
name: "Vendors",
|
||||
columns: table => new
|
||||
{
|
||||
Id = table.Column<int>(type: "int", nullable: false)
|
||||
.Annotation("SqlServer:Identity", "1, 1"),
|
||||
CompanyName = table.Column<string>(type: "nvarchar(max)", nullable: true),
|
||||
ContactName = table.Column<string>(type: "nvarchar(max)", nullable: true),
|
||||
Email = table.Column<string>(type: "nvarchar(max)", nullable: true),
|
||||
Phone = table.Column<string>(type: "nvarchar(max)", nullable: true),
|
||||
Address = table.Column<string>(type: "nvarchar(max)", nullable: true),
|
||||
City = table.Column<string>(type: "nvarchar(max)", nullable: true),
|
||||
State = table.Column<string>(type: "nvarchar(max)", nullable: true),
|
||||
Zip = table.Column<string>(type: "nvarchar(max)", nullable: true),
|
||||
TradeSpecialties = table.Column<string>(type: "nvarchar(max)", nullable: true),
|
||||
IsActive = table.Column<bool>(type: "bit", nullable: false),
|
||||
IsDeleted = table.Column<bool>(type: "bit", nullable: true),
|
||||
createdby = table.Column<string>(type: "nvarchar(max)", nullable: true),
|
||||
DeleterUserId = table.Column<string>(type: "nvarchar(max)", nullable: true),
|
||||
DeletionTime = table.Column<DateTime>(type: "datetime2", nullable: true),
|
||||
CreatedDate = table.Column<DateTime>(type: "datetime2", nullable: true),
|
||||
LastModificationTime = table.Column<DateTime>(type: "datetime2", nullable: true),
|
||||
LastModifierUserId = table.Column<int>(type: "int", nullable: true)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_Vendors", x => x.Id);
|
||||
});
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
name: "Dispatches",
|
||||
columns: table => new
|
||||
{
|
||||
Id = table.Column<int>(type: "int", nullable: false)
|
||||
.Annotation("SqlServer:Identity", "1, 1"),
|
||||
WorkOrderId = table.Column<int>(type: "int", nullable: false),
|
||||
VendorId = table.Column<int>(type: "int", nullable: false),
|
||||
PONumber = table.Column<string>(type: "nvarchar(max)", nullable: true),
|
||||
NTEAmount = table.Column<decimal>(type: "decimal(18,2)", nullable: true),
|
||||
Description = table.Column<string>(type: "nvarchar(max)", nullable: true),
|
||||
Status = table.Column<string>(type: "nvarchar(max)", nullable: true),
|
||||
DispatchedAt = table.Column<DateTime>(type: "datetime2", nullable: true),
|
||||
EmailSent = table.Column<bool>(type: "bit", nullable: false),
|
||||
ReplyToAddress = table.Column<string>(type: "nvarchar(max)", nullable: true),
|
||||
IsDeleted = table.Column<bool>(type: "bit", nullable: true),
|
||||
createdby = table.Column<string>(type: "nvarchar(max)", nullable: true),
|
||||
DeleterUserId = table.Column<string>(type: "nvarchar(max)", nullable: true),
|
||||
DeletionTime = table.Column<DateTime>(type: "datetime2", nullable: true),
|
||||
CreatedDate = table.Column<DateTime>(type: "datetime2", nullable: true),
|
||||
LastModificationTime = table.Column<DateTime>(type: "datetime2", nullable: true),
|
||||
LastModifierUserId = table.Column<int>(type: "int", nullable: true)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_Dispatches", x => x.Id);
|
||||
table.ForeignKey(
|
||||
name: "FK_Dispatches_Vendors_VendorId",
|
||||
column: x => x.VendorId,
|
||||
principalTable: "Vendors",
|
||||
principalColumn: "Id",
|
||||
onDelete: ReferentialAction.Restrict);
|
||||
table.ForeignKey(
|
||||
name: "FK_Dispatches_workOrders_WorkOrderId",
|
||||
column: x => x.WorkOrderId,
|
||||
principalTable: "workOrders",
|
||||
principalColumn: "Id",
|
||||
onDelete: ReferentialAction.Restrict);
|
||||
});
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_Dispatches_VendorId",
|
||||
table: "Dispatches",
|
||||
column: "VendorId");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_Dispatches_WorkOrderId",
|
||||
table: "Dispatches",
|
||||
column: "WorkOrderId");
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropTable(
|
||||
name: "Dispatches");
|
||||
|
||||
migrationBuilder.DropTable(
|
||||
name: "Vendors");
|
||||
}
|
||||
}
|
||||
}
|
||||
2481
Data.SeaHavenIndustries/Migrations/20260417154641_AddDispatchScheduledDate.Designer.cs
generated
Normal file
2481
Data.SeaHavenIndustries/Migrations/20260417154641_AddDispatchScheduledDate.Designer.cs
generated
Normal file
File diff suppressed because it is too large
Load diff
|
|
@ -0,0 +1,29 @@
|
|||
using System;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace Data.SeaHavenIndustries.Migrations
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public partial class AddDispatchScheduledDate : Migration
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.AddColumn<DateTime>(
|
||||
name: "ScheduledDate",
|
||||
table: "Dispatches",
|
||||
type: "datetime2",
|
||||
nullable: true);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropColumn(
|
||||
name: "ScheduledDate",
|
||||
table: "Dispatches");
|
||||
}
|
||||
}
|
||||
}
|
||||
2504
Data.SeaHavenIndustries/Migrations/20260417171152_AddDispatchNumberAndCommentDispatchId.Designer.cs
generated
Normal file
2504
Data.SeaHavenIndustries/Migrations/20260417171152_AddDispatchNumberAndCommentDispatchId.Designer.cs
generated
Normal file
File diff suppressed because it is too large
Load diff
|
|
@ -0,0 +1,70 @@
|
|||
using System;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace Data.SeaHavenIndustries.Migrations
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public partial class AddDispatchNumberAndCommentDispatchId : Migration
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.AddColumn<DateTime>(
|
||||
name: "CompletedDate",
|
||||
table: "Dispatches",
|
||||
type: "datetime2",
|
||||
nullable: true);
|
||||
|
||||
migrationBuilder.AddColumn<string>(
|
||||
name: "DispatchNumber",
|
||||
table: "Dispatches",
|
||||
type: "nvarchar(max)",
|
||||
nullable: true);
|
||||
|
||||
migrationBuilder.AddColumn<int>(
|
||||
name: "DispatchId",
|
||||
table: "Comments",
|
||||
type: "int",
|
||||
nullable: true);
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_Comments_DispatchId",
|
||||
table: "Comments",
|
||||
column: "DispatchId");
|
||||
|
||||
migrationBuilder.AddForeignKey(
|
||||
name: "FK_Comments_Dispatches_DispatchId",
|
||||
table: "Comments",
|
||||
column: "DispatchId",
|
||||
principalTable: "Dispatches",
|
||||
principalColumn: "Id",
|
||||
onDelete: ReferentialAction.Restrict);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropForeignKey(
|
||||
name: "FK_Comments_Dispatches_DispatchId",
|
||||
table: "Comments");
|
||||
|
||||
migrationBuilder.DropIndex(
|
||||
name: "IX_Comments_DispatchId",
|
||||
table: "Comments");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "CompletedDate",
|
||||
table: "Dispatches");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "DispatchNumber",
|
||||
table: "Dispatches");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "DispatchId",
|
||||
table: "Comments");
|
||||
}
|
||||
}
|
||||
}
|
||||
2608
Data.SeaHavenIndustries/Migrations/20260417182910_AddTaskListTemplates.Designer.cs
generated
Normal file
2608
Data.SeaHavenIndustries/Migrations/20260417182910_AddTaskListTemplates.Designer.cs
generated
Normal file
File diff suppressed because it is too large
Load diff
|
|
@ -0,0 +1,80 @@
|
|||
using System;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace Data.SeaHavenIndustries.Migrations
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public partial class AddTaskListTemplates : Migration
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.CreateTable(
|
||||
name: "TaskListTemplates",
|
||||
columns: table => new
|
||||
{
|
||||
Id = table.Column<int>(type: "int", nullable: false)
|
||||
.Annotation("SqlServer:Identity", "1, 1"),
|
||||
Name = table.Column<string>(type: "nvarchar(max)", nullable: true),
|
||||
Description = table.Column<string>(type: "nvarchar(max)", nullable: true),
|
||||
IsActive = table.Column<bool>(type: "bit", nullable: false),
|
||||
IsDeleted = table.Column<bool>(type: "bit", nullable: true),
|
||||
createdby = table.Column<string>(type: "nvarchar(max)", nullable: true),
|
||||
DeleterUserId = table.Column<string>(type: "nvarchar(max)", nullable: true),
|
||||
DeletionTime = table.Column<DateTime>(type: "datetime2", nullable: true),
|
||||
CreatedDate = table.Column<DateTime>(type: "datetime2", nullable: true),
|
||||
LastModificationTime = table.Column<DateTime>(type: "datetime2", nullable: true),
|
||||
LastModifierUserId = table.Column<int>(type: "int", nullable: true)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_TaskListTemplates", x => x.Id);
|
||||
});
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
name: "TaskListTemplateItems",
|
||||
columns: table => new
|
||||
{
|
||||
Id = table.Column<int>(type: "int", nullable: false)
|
||||
.Annotation("SqlServer:Identity", "1, 1"),
|
||||
TaskListTemplateId = table.Column<int>(type: "int", nullable: false),
|
||||
ItemText = table.Column<string>(type: "nvarchar(max)", nullable: true),
|
||||
SortOrder = table.Column<int>(type: "int", nullable: false),
|
||||
IsDeleted = table.Column<bool>(type: "bit", nullable: true),
|
||||
createdby = table.Column<string>(type: "nvarchar(max)", nullable: true),
|
||||
DeleterUserId = table.Column<string>(type: "nvarchar(max)", nullable: true),
|
||||
DeletionTime = table.Column<DateTime>(type: "datetime2", nullable: true),
|
||||
CreatedDate = table.Column<DateTime>(type: "datetime2", nullable: true),
|
||||
LastModificationTime = table.Column<DateTime>(type: "datetime2", nullable: true),
|
||||
LastModifierUserId = table.Column<int>(type: "int", nullable: true)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_TaskListTemplateItems", x => x.Id);
|
||||
table.ForeignKey(
|
||||
name: "FK_TaskListTemplateItems_TaskListTemplates_TaskListTemplateId",
|
||||
column: x => x.TaskListTemplateId,
|
||||
principalTable: "TaskListTemplates",
|
||||
principalColumn: "Id",
|
||||
onDelete: ReferentialAction.Restrict);
|
||||
});
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_TaskListTemplateItems_TaskListTemplateId",
|
||||
table: "TaskListTemplateItems",
|
||||
column: "TaskListTemplateId");
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropTable(
|
||||
name: "TaskListTemplateItems");
|
||||
|
||||
migrationBuilder.DropTable(
|
||||
name: "TaskListTemplates");
|
||||
}
|
||||
}
|
||||
}
|
||||
2657
Data.SeaHavenIndustries/Migrations/20260417190743_MultiWODispatch.Designer.cs
generated
Normal file
2657
Data.SeaHavenIndustries/Migrations/20260417190743_MultiWODispatch.Designer.cs
generated
Normal file
File diff suppressed because it is too large
Load diff
|
|
@ -0,0 +1,96 @@
|
|||
using System;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace Data.SeaHavenIndustries.Migrations
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public partial class MultiWODispatch : Migration
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.AlterColumn<int>(
|
||||
name: "WorkOrderId",
|
||||
table: "Dispatches",
|
||||
type: "int",
|
||||
nullable: true,
|
||||
oldClrType: typeof(int),
|
||||
oldType: "int");
|
||||
|
||||
migrationBuilder.AddColumn<string>(
|
||||
name: "AcceptToken",
|
||||
table: "Dispatches",
|
||||
type: "nvarchar(max)",
|
||||
nullable: true);
|
||||
|
||||
migrationBuilder.AddColumn<DateTime>(
|
||||
name: "AcknowledgedAt",
|
||||
table: "Dispatches",
|
||||
type: "datetime2",
|
||||
nullable: true);
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
name: "DispatchWorkOrders",
|
||||
columns: table => new
|
||||
{
|
||||
Id = table.Column<int>(type: "int", nullable: false)
|
||||
.Annotation("SqlServer:Identity", "1, 1"),
|
||||
DispatchId = table.Column<int>(type: "int", nullable: false),
|
||||
WorkOrderId = table.Column<int>(type: "int", nullable: false)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_DispatchWorkOrders", x => x.Id);
|
||||
table.ForeignKey(
|
||||
name: "FK_DispatchWorkOrders_Dispatches_DispatchId",
|
||||
column: x => x.DispatchId,
|
||||
principalTable: "Dispatches",
|
||||
principalColumn: "Id",
|
||||
onDelete: ReferentialAction.Restrict);
|
||||
table.ForeignKey(
|
||||
name: "FK_DispatchWorkOrders_workOrders_WorkOrderId",
|
||||
column: x => x.WorkOrderId,
|
||||
principalTable: "workOrders",
|
||||
principalColumn: "Id",
|
||||
onDelete: ReferentialAction.Restrict);
|
||||
});
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_DispatchWorkOrders_DispatchId",
|
||||
table: "DispatchWorkOrders",
|
||||
column: "DispatchId");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_DispatchWorkOrders_WorkOrderId",
|
||||
table: "DispatchWorkOrders",
|
||||
column: "WorkOrderId");
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropTable(
|
||||
name: "DispatchWorkOrders");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "AcceptToken",
|
||||
table: "Dispatches");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "AcknowledgedAt",
|
||||
table: "Dispatches");
|
||||
|
||||
migrationBuilder.AlterColumn<int>(
|
||||
name: "WorkOrderId",
|
||||
table: "Dispatches",
|
||||
type: "int",
|
||||
nullable: false,
|
||||
defaultValue: 0,
|
||||
oldClrType: typeof(int),
|
||||
oldType: "int",
|
||||
oldNullable: true);
|
||||
}
|
||||
}
|
||||
}
|
||||
2736
Data.SeaHavenIndustries/Migrations/20260417192012_AddDispatchChecklist.Designer.cs
generated
Normal file
2736
Data.SeaHavenIndustries/Migrations/20260417192012_AddDispatchChecklist.Designer.cs
generated
Normal file
File diff suppressed because it is too large
Load diff
|
|
@ -0,0 +1,70 @@
|
|||
using System;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace Data.SeaHavenIndustries.Migrations
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public partial class AddDispatchChecklist : Migration
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.CreateTable(
|
||||
name: "DispatchChecklistItems",
|
||||
columns: table => new
|
||||
{
|
||||
Id = table.Column<int>(type: "int", nullable: false)
|
||||
.Annotation("SqlServer:Identity", "1, 1"),
|
||||
DispatchId = table.Column<int>(type: "int", nullable: false),
|
||||
WorkOrderId = table.Column<int>(type: "int", nullable: true),
|
||||
ItemText = table.Column<string>(type: "nvarchar(max)", nullable: true),
|
||||
IsCompleted = table.Column<bool>(type: "bit", nullable: false),
|
||||
CompletedBy = table.Column<string>(type: "nvarchar(max)", nullable: true),
|
||||
CompletedAt = table.Column<DateTime>(type: "datetime2", nullable: true),
|
||||
SortOrder = table.Column<int>(type: "int", nullable: false),
|
||||
IsDeleted = table.Column<bool>(type: "bit", nullable: true),
|
||||
createdby = table.Column<string>(type: "nvarchar(max)", nullable: true),
|
||||
DeleterUserId = table.Column<string>(type: "nvarchar(max)", nullable: true),
|
||||
DeletionTime = table.Column<DateTime>(type: "datetime2", nullable: true),
|
||||
CreatedDate = table.Column<DateTime>(type: "datetime2", nullable: true),
|
||||
LastModificationTime = table.Column<DateTime>(type: "datetime2", nullable: true),
|
||||
LastModifierUserId = table.Column<int>(type: "int", nullable: true)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_DispatchChecklistItems", x => x.Id);
|
||||
table.ForeignKey(
|
||||
name: "FK_DispatchChecklistItems_Dispatches_DispatchId",
|
||||
column: x => x.DispatchId,
|
||||
principalTable: "Dispatches",
|
||||
principalColumn: "Id",
|
||||
onDelete: ReferentialAction.Restrict);
|
||||
table.ForeignKey(
|
||||
name: "FK_DispatchChecklistItems_workOrders_WorkOrderId",
|
||||
column: x => x.WorkOrderId,
|
||||
principalTable: "workOrders",
|
||||
principalColumn: "Id",
|
||||
onDelete: ReferentialAction.Restrict);
|
||||
});
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_DispatchChecklistItems_DispatchId",
|
||||
table: "DispatchChecklistItems",
|
||||
column: "DispatchId");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_DispatchChecklistItems_WorkOrderId",
|
||||
table: "DispatchChecklistItems",
|
||||
column: "WorkOrderId");
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropTable(
|
||||
name: "DispatchChecklistItems");
|
||||
}
|
||||
}
|
||||
}
|
||||
2803
Data.SeaHavenIndustries/Migrations/20260417194438_AddDispatchSignoffs.Designer.cs
generated
Normal file
2803
Data.SeaHavenIndustries/Migrations/20260417194438_AddDispatchSignoffs.Designer.cs
generated
Normal file
File diff suppressed because it is too large
Load diff
|
|
@ -0,0 +1,58 @@
|
|||
using System;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace Data.SeaHavenIndustries.Migrations
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public partial class AddDispatchSignoffs : Migration
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.CreateTable(
|
||||
name: "DispatchSignoffs",
|
||||
columns: table => new
|
||||
{
|
||||
Id = table.Column<int>(type: "int", nullable: false)
|
||||
.Annotation("SqlServer:Identity", "1, 1"),
|
||||
DispatchId = table.Column<int>(type: "int", nullable: false),
|
||||
SignoffType = table.Column<string>(type: "nvarchar(max)", nullable: true),
|
||||
Name = table.Column<string>(type: "nvarchar(max)", nullable: true),
|
||||
Signature = table.Column<string>(type: "nvarchar(max)", nullable: true),
|
||||
SignatureMethod = table.Column<string>(type: "nvarchar(max)", nullable: true),
|
||||
SignedAt = table.Column<DateTime>(type: "datetime2", nullable: true),
|
||||
IsDeleted = table.Column<bool>(type: "bit", nullable: true),
|
||||
createdby = table.Column<string>(type: "nvarchar(max)", nullable: true),
|
||||
DeleterUserId = table.Column<string>(type: "nvarchar(max)", nullable: true),
|
||||
DeletionTime = table.Column<DateTime>(type: "datetime2", nullable: true),
|
||||
CreatedDate = table.Column<DateTime>(type: "datetime2", nullable: true),
|
||||
LastModificationTime = table.Column<DateTime>(type: "datetime2", nullable: true),
|
||||
LastModifierUserId = table.Column<int>(type: "int", nullable: true)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_DispatchSignoffs", x => x.Id);
|
||||
table.ForeignKey(
|
||||
name: "FK_DispatchSignoffs_Dispatches_DispatchId",
|
||||
column: x => x.DispatchId,
|
||||
principalTable: "Dispatches",
|
||||
principalColumn: "Id",
|
||||
onDelete: ReferentialAction.Restrict);
|
||||
});
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_DispatchSignoffs_DispatchId",
|
||||
table: "DispatchSignoffs",
|
||||
column: "DispatchId");
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropTable(
|
||||
name: "DispatchSignoffs");
|
||||
}
|
||||
}
|
||||
}
|
||||
2809
Data.SeaHavenIndustries/Migrations/20260417195316_AddDispatchVerification.Designer.cs
generated
Normal file
2809
Data.SeaHavenIndustries/Migrations/20260417195316_AddDispatchVerification.Designer.cs
generated
Normal file
File diff suppressed because it is too large
Load diff
|
|
@ -0,0 +1,39 @@
|
|||
using System;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace Data.SeaHavenIndustries.Migrations
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public partial class AddDispatchVerification : Migration
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.AddColumn<DateTime>(
|
||||
name: "VerifiedAt",
|
||||
table: "Dispatches",
|
||||
type: "datetime2",
|
||||
nullable: true);
|
||||
|
||||
migrationBuilder.AddColumn<string>(
|
||||
name: "VerifiedBy",
|
||||
table: "Dispatches",
|
||||
type: "nvarchar(max)",
|
||||
nullable: true);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropColumn(
|
||||
name: "VerifiedAt",
|
||||
table: "Dispatches");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "VerifiedBy",
|
||||
table: "Dispatches");
|
||||
}
|
||||
}
|
||||
}
|
||||
2879
Data.SeaHavenIndustries/Migrations/20260420151445_AddVendorAccessToken.Designer.cs
generated
Normal file
2879
Data.SeaHavenIndustries/Migrations/20260420151445_AddVendorAccessToken.Designer.cs
generated
Normal file
File diff suppressed because it is too large
Load diff
|
|
@ -0,0 +1,64 @@
|
|||
using System;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace Data.SeaHavenIndustries.Migrations
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public partial class AddVendorAccessToken : Migration
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.CreateTable(
|
||||
name: "VendorAccessTokens",
|
||||
columns: table => new
|
||||
{
|
||||
Id = table.Column<int>(type: "int", nullable: false)
|
||||
.Annotation("SqlServer:Identity", "1, 1"),
|
||||
VendorId = table.Column<int>(type: "int", nullable: false),
|
||||
Token = table.Column<string>(type: "nvarchar(64)", maxLength: 64, nullable: false),
|
||||
IssuedAt = table.Column<DateTime>(type: "datetime2", nullable: false),
|
||||
ExpiresAt = table.Column<DateTime>(type: "datetime2", nullable: false),
|
||||
RevokedAt = table.Column<DateTime>(type: "datetime2", nullable: true),
|
||||
LastUsedAt = table.Column<DateTime>(type: "datetime2", nullable: true),
|
||||
IsDeleted = table.Column<bool>(type: "bit", nullable: true),
|
||||
createdby = table.Column<string>(type: "nvarchar(max)", nullable: true),
|
||||
DeleterUserId = table.Column<string>(type: "nvarchar(max)", nullable: true),
|
||||
DeletionTime = table.Column<DateTime>(type: "datetime2", nullable: true),
|
||||
CreatedDate = table.Column<DateTime>(type: "datetime2", nullable: true),
|
||||
LastModificationTime = table.Column<DateTime>(type: "datetime2", nullable: true),
|
||||
LastModifierUserId = table.Column<int>(type: "int", nullable: true)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_VendorAccessTokens", x => x.Id);
|
||||
table.ForeignKey(
|
||||
name: "FK_VendorAccessTokens_Vendors_VendorId",
|
||||
column: x => x.VendorId,
|
||||
principalTable: "Vendors",
|
||||
principalColumn: "Id",
|
||||
onDelete: ReferentialAction.Restrict);
|
||||
});
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_VendorAccessTokens_Token",
|
||||
table: "VendorAccessTokens",
|
||||
column: "Token",
|
||||
unique: true);
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_VendorAccessTokens_VendorId",
|
||||
table: "VendorAccessTokens",
|
||||
column: "VendorId");
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropTable(
|
||||
name: "VendorAccessTokens");
|
||||
}
|
||||
}
|
||||
}
|
||||
2960
Data.SeaHavenIndustries/Migrations/20260420164708_AddDispatchUpliftRequest.Designer.cs
generated
Normal file
2960
Data.SeaHavenIndustries/Migrations/20260420164708_AddDispatchUpliftRequest.Designer.cs
generated
Normal file
File diff suppressed because it is too large
Load diff
|
|
@ -0,0 +1,64 @@
|
|||
using System;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace Data.SeaHavenIndustries.Migrations
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public partial class AddDispatchUpliftRequest : Migration
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.CreateTable(
|
||||
name: "DispatchUpliftRequests",
|
||||
columns: table => new
|
||||
{
|
||||
Id = table.Column<int>(type: "int", nullable: false)
|
||||
.Annotation("SqlServer:Identity", "1, 1"),
|
||||
DispatchId = table.Column<int>(type: "int", nullable: false),
|
||||
CurrentNTE = table.Column<decimal>(type: "decimal(18,2)", nullable: true),
|
||||
RequestedNTE = table.Column<decimal>(type: "decimal(18,2)", nullable: false),
|
||||
VendorReason = table.Column<string>(type: "nvarchar(max)", nullable: true),
|
||||
Status = table.Column<string>(type: "nvarchar(20)", maxLength: 20, nullable: false),
|
||||
RequiredTier = table.Column<int>(type: "int", nullable: false),
|
||||
RequestedByVendorName = table.Column<string>(type: "nvarchar(max)", nullable: true),
|
||||
DecidedAt = table.Column<DateTime>(type: "datetime2", nullable: true),
|
||||
DecidedByUserId = table.Column<string>(type: "nvarchar(max)", nullable: true),
|
||||
DecisionNote = table.Column<string>(type: "nvarchar(max)", nullable: true),
|
||||
IsDeleted = table.Column<bool>(type: "bit", nullable: true),
|
||||
createdby = table.Column<string>(type: "nvarchar(max)", nullable: true),
|
||||
DeleterUserId = table.Column<string>(type: "nvarchar(max)", nullable: true),
|
||||
DeletionTime = table.Column<DateTime>(type: "datetime2", nullable: true),
|
||||
CreatedDate = table.Column<DateTime>(type: "datetime2", nullable: true),
|
||||
LastModificationTime = table.Column<DateTime>(type: "datetime2", nullable: true),
|
||||
LastModifierUserId = table.Column<int>(type: "int", nullable: true)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_DispatchUpliftRequests", x => x.Id);
|
||||
table.ForeignKey(
|
||||
name: "FK_DispatchUpliftRequests_Dispatches_DispatchId",
|
||||
column: x => x.DispatchId,
|
||||
principalTable: "Dispatches",
|
||||
principalColumn: "Id",
|
||||
onDelete: ReferentialAction.Restrict);
|
||||
});
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_DispatchUpliftRequests_DispatchId_Status",
|
||||
table: "DispatchUpliftRequests",
|
||||
columns: new[] { "DispatchId", "Status" },
|
||||
unique: true,
|
||||
filter: "[Status] = 'Pending'");
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropTable(
|
||||
name: "DispatchUpliftRequests");
|
||||
}
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load diff
|
|
@ -7,6 +7,10 @@ namespace Data.SeaHavenIndustries
|
|||
public class Comments : FullAuditEntity
|
||||
{
|
||||
public string? Commenttext { get; set; }
|
||||
public string? Commenter { get; set; }
|
||||
public string? RecordType { get; set; }
|
||||
public string? CommentType { get; set; }
|
||||
public string? ExternalCommentId { get; set; }
|
||||
public string? Documents { get; set; }
|
||||
public string? UserId { get; set; }
|
||||
[ForeignKey(nameof(UserId))]
|
||||
|
|
@ -14,5 +18,8 @@ namespace Data.SeaHavenIndustries
|
|||
public int? WorkerOrderId { get; set; }
|
||||
[ForeignKey(nameof(WorkerOrderId))]
|
||||
public virtual WorkOrder? WorkOrder { get; set; }
|
||||
public int? DispatchId { get; set; }
|
||||
[ForeignKey(nameof(DispatchId))]
|
||||
public virtual Dispatch? Dispatch { get; set; }
|
||||
}
|
||||
}
|
||||
|
|
|
|||
34
Data.SeaHavenIndustries/Models/Dispatch.cs
Normal file
34
Data.SeaHavenIndustries/Models/Dispatch.cs
Normal file
|
|
@ -0,0 +1,34 @@
|
|||
using System.ComponentModel.DataAnnotations.Schema;
|
||||
|
||||
namespace Data.SeaHavenIndustries
|
||||
{
|
||||
public class Dispatch : FullAuditEntity
|
||||
{
|
||||
public int? WorkOrderId { get; set; }
|
||||
[ForeignKey(nameof(WorkOrderId))]
|
||||
public virtual WorkOrder? WorkOrder { get; set; }
|
||||
|
||||
public int VendorId { get; set; }
|
||||
[ForeignKey(nameof(VendorId))]
|
||||
public virtual Vendor? Vendor { get; set; }
|
||||
|
||||
public string? DispatchNumber { get; set; }
|
||||
public string? PONumber { get; set; }
|
||||
public decimal? NTEAmount { get; set; }
|
||||
public string? Description { get; set; }
|
||||
public string? Status { get; set; }
|
||||
public DateTime? ScheduledDate { get; set; }
|
||||
public DateTime? CompletedDate { get; set; }
|
||||
public DateTime? DispatchedAt { get; set; }
|
||||
public DateTime? AcknowledgedAt { get; set; }
|
||||
public bool EmailSent { get; set; }
|
||||
public string? ReplyToAddress { get; set; }
|
||||
public string? AcceptToken { get; set; }
|
||||
public string? VerifiedBy { get; set; }
|
||||
public DateTime? VerifiedAt { get; set; }
|
||||
public ICollection<Comments>? Comments { get; set; }
|
||||
public ICollection<DispatchWorkOrder>? DispatchWorkOrders { get; set; }
|
||||
public ICollection<DispatchChecklistItem>? ChecklistItems { get; set; }
|
||||
public ICollection<DispatchSignoff>? Signoffs { get; set; }
|
||||
}
|
||||
}
|
||||
19
Data.SeaHavenIndustries/Models/DispatchChecklistItem.cs
Normal file
19
Data.SeaHavenIndustries/Models/DispatchChecklistItem.cs
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
using System.ComponentModel.DataAnnotations.Schema;
|
||||
|
||||
namespace Data.SeaHavenIndustries
|
||||
{
|
||||
public class DispatchChecklistItem : FullAuditEntity
|
||||
{
|
||||
public int DispatchId { get; set; }
|
||||
[ForeignKey(nameof(DispatchId))]
|
||||
public virtual Dispatch? Dispatch { get; set; }
|
||||
public int? WorkOrderId { get; set; }
|
||||
[ForeignKey(nameof(WorkOrderId))]
|
||||
public virtual WorkOrder? WorkOrder { get; set; }
|
||||
public string? ItemText { get; set; }
|
||||
public bool IsCompleted { get; set; }
|
||||
public string? CompletedBy { get; set; }
|
||||
public DateTime? CompletedAt { get; set; }
|
||||
public int SortOrder { get; set; }
|
||||
}
|
||||
}
|
||||
16
Data.SeaHavenIndustries/Models/DispatchSignoff.cs
Normal file
16
Data.SeaHavenIndustries/Models/DispatchSignoff.cs
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
using System.ComponentModel.DataAnnotations.Schema;
|
||||
|
||||
namespace Data.SeaHavenIndustries
|
||||
{
|
||||
public class DispatchSignoff : FullAuditEntity
|
||||
{
|
||||
public int DispatchId { get; set; }
|
||||
[ForeignKey(nameof(DispatchId))]
|
||||
public virtual Dispatch? Dispatch { get; set; }
|
||||
public string? SignoffType { get; set; }
|
||||
public string? Name { get; set; }
|
||||
public string? Signature { get; set; }
|
||||
public string? SignatureMethod { get; set; }
|
||||
public DateTime? SignedAt { get; set; }
|
||||
}
|
||||
}
|
||||
32
Data.SeaHavenIndustries/Models/DispatchUpliftRequest.cs
Normal file
32
Data.SeaHavenIndustries/Models/DispatchUpliftRequest.cs
Normal file
|
|
@ -0,0 +1,32 @@
|
|||
using System.ComponentModel.DataAnnotations;
|
||||
using System.ComponentModel.DataAnnotations.Schema;
|
||||
|
||||
namespace Data.SeaHavenIndustries
|
||||
{
|
||||
public class DispatchUpliftRequest : FullAuditEntity
|
||||
{
|
||||
public int DispatchId { get; set; }
|
||||
[ForeignKey(nameof(DispatchId))]
|
||||
public virtual Dispatch? Dispatch { get; set; }
|
||||
|
||||
[Column(TypeName = "decimal(18,2)")]
|
||||
public decimal? CurrentNTE { get; set; }
|
||||
|
||||
[Column(TypeName = "decimal(18,2)")]
|
||||
public decimal RequestedNTE { get; set; }
|
||||
|
||||
public string? VendorReason { get; set; }
|
||||
|
||||
[Required]
|
||||
[MaxLength(20)]
|
||||
public string Status { get; set; } = "Pending";
|
||||
|
||||
public int RequiredTier { get; set; }
|
||||
|
||||
public string? RequestedByVendorName { get; set; }
|
||||
|
||||
public DateTime? DecidedAt { get; set; }
|
||||
public string? DecidedByUserId { get; set; }
|
||||
public string? DecisionNote { get; set; }
|
||||
}
|
||||
}
|
||||
15
Data.SeaHavenIndustries/Models/DispatchWorkOrder.cs
Normal file
15
Data.SeaHavenIndustries/Models/DispatchWorkOrder.cs
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
using System.ComponentModel.DataAnnotations.Schema;
|
||||
|
||||
namespace Data.SeaHavenIndustries
|
||||
{
|
||||
public class DispatchWorkOrder
|
||||
{
|
||||
public int Id { get; set; }
|
||||
public int DispatchId { get; set; }
|
||||
[ForeignKey(nameof(DispatchId))]
|
||||
public virtual Dispatch? Dispatch { get; set; }
|
||||
public int WorkOrderId { get; set; }
|
||||
[ForeignKey(nameof(WorkOrderId))]
|
||||
public virtual WorkOrder? WorkOrder { get; set; }
|
||||
}
|
||||
}
|
||||
12
Data.SeaHavenIndustries/Models/DropdownOption.cs
Normal file
12
Data.SeaHavenIndustries/Models/DropdownOption.cs
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
namespace Data.SeaHavenIndustries
|
||||
{
|
||||
public class DropdownOption
|
||||
{
|
||||
public int Id { get; set; }
|
||||
public string Category { get; set; } = "";
|
||||
public string Value { get; set; } = "";
|
||||
public string? ParentValue { get; set; }
|
||||
public int SortOrder { get; set; }
|
||||
public bool IsActive { get; set; } = true;
|
||||
}
|
||||
}
|
||||
|
|
@ -86,8 +86,9 @@ namespace Data.SeaHavenIndustries
|
|||
public Employee Employee { get; set; } = null!;
|
||||
}
|
||||
|
||||
public class EmployeeEmail : FullAuditEntity
|
||||
public class EmployeeEmail
|
||||
{
|
||||
public int Id { get; set; }
|
||||
public int EmployeeId { get; set; }
|
||||
public Employee? Employee { get; set; }
|
||||
|
||||
|
|
@ -99,22 +100,28 @@ namespace Data.SeaHavenIndustries
|
|||
public bool? IsPrimary { get; set; }
|
||||
}
|
||||
|
||||
public class Department : FullAuditEntity
|
||||
public class Department
|
||||
{
|
||||
public int Id { get; set; }
|
||||
|
||||
[MaxLength(200)]
|
||||
public string Name { get; set; } = null!;
|
||||
public ICollection<Employee>? Employees { get; set; }
|
||||
}
|
||||
|
||||
public class JobTitle : FullAuditEntity
|
||||
public class JobTitle
|
||||
{
|
||||
public int Id { get; set; }
|
||||
|
||||
[MaxLength(200)]
|
||||
public string Name { get; set; } = null!;
|
||||
public ICollection<Employee>? Employees { get; set; }
|
||||
}
|
||||
|
||||
public class Region : FullAuditEntity
|
||||
public class Region
|
||||
{
|
||||
public int Id { get; set; }
|
||||
|
||||
[MaxLength(200)]
|
||||
public string Name { get; set; } = null!;
|
||||
public ICollection<Employee>? Employees { get; set; }
|
||||
|
|
|
|||
|
|
@ -6,12 +6,26 @@ namespace Data.SeaHavenIndustries
|
|||
{
|
||||
public class Locations : FullAuditEntity
|
||||
{
|
||||
public int Id { get; set; }
|
||||
public string? Title { get; set; }
|
||||
public string? Name { get; set; }
|
||||
public string? Latitude { get; set; }
|
||||
public string? Longitude { get; set; }
|
||||
|
||||
// Address fields - match actual database columns
|
||||
public string? Address1 { get; set; }
|
||||
public string? Address2 { get; set; }
|
||||
public string? City { get; set; }
|
||||
public string? State { get; set; }
|
||||
public string? Zip { get; set; }
|
||||
|
||||
// Contact fields - match actual database columns
|
||||
public string? PhoneNumber { get; set; }
|
||||
public string? Email { get; set; }
|
||||
public string? Status { get; set; }
|
||||
|
||||
// Navigation Properties
|
||||
public ICollection<Template>? Templates { get; set; }
|
||||
public ICollection<WorkOrder>? workOrders { get; set; }
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
|||
10
Data.SeaHavenIndustries/Models/TaskListTemplate.cs
Normal file
10
Data.SeaHavenIndustries/Models/TaskListTemplate.cs
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
namespace Data.SeaHavenIndustries
|
||||
{
|
||||
public class TaskListTemplate : FullAuditEntity
|
||||
{
|
||||
public string? Name { get; set; }
|
||||
public string? Description { get; set; }
|
||||
public bool IsActive { get; set; } = true;
|
||||
public ICollection<TaskListTemplateItem>? Items { get; set; }
|
||||
}
|
||||
}
|
||||
13
Data.SeaHavenIndustries/Models/TaskListTemplateItem.cs
Normal file
13
Data.SeaHavenIndustries/Models/TaskListTemplateItem.cs
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
using System.ComponentModel.DataAnnotations.Schema;
|
||||
|
||||
namespace Data.SeaHavenIndustries
|
||||
{
|
||||
public class TaskListTemplateItem : FullAuditEntity
|
||||
{
|
||||
public int TaskListTemplateId { get; set; }
|
||||
[ForeignKey(nameof(TaskListTemplateId))]
|
||||
public virtual TaskListTemplate? Template { get; set; }
|
||||
public string? ItemText { get; set; }
|
||||
public int SortOrder { get; set; }
|
||||
}
|
||||
}
|
||||
17
Data.SeaHavenIndustries/Models/Vendor.cs
Normal file
17
Data.SeaHavenIndustries/Models/Vendor.cs
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
namespace Data.SeaHavenIndustries
|
||||
{
|
||||
public class Vendor : FullAuditEntity
|
||||
{
|
||||
public string? CompanyName { get; set; }
|
||||
public string? ContactName { get; set; }
|
||||
public string? Email { get; set; }
|
||||
public string? Phone { get; set; }
|
||||
public string? Address { get; set; }
|
||||
public string? City { get; set; }
|
||||
public string? State { get; set; }
|
||||
public string? Zip { get; set; }
|
||||
public string? TradeSpecialties { get; set; }
|
||||
public bool IsActive { get; set; } = true;
|
||||
public ICollection<Dispatch>? Dispatches { get; set; }
|
||||
}
|
||||
}
|
||||
21
Data.SeaHavenIndustries/Models/VendorAccessToken.cs
Normal file
21
Data.SeaHavenIndustries/Models/VendorAccessToken.cs
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
using System.ComponentModel.DataAnnotations;
|
||||
using System.ComponentModel.DataAnnotations.Schema;
|
||||
|
||||
namespace Data.SeaHavenIndustries
|
||||
{
|
||||
public class VendorAccessToken : FullAuditEntity
|
||||
{
|
||||
public int VendorId { get; set; }
|
||||
[ForeignKey(nameof(VendorId))]
|
||||
public virtual Vendor? Vendor { get; set; }
|
||||
|
||||
[Required]
|
||||
[MaxLength(64)]
|
||||
public string Token { get; set; } = string.Empty;
|
||||
|
||||
public DateTime IssuedAt { get; set; }
|
||||
public DateTime ExpiresAt { get; set; }
|
||||
public DateTime? RevokedAt { get; set; }
|
||||
public DateTime? LastUsedAt { get; set; }
|
||||
}
|
||||
}
|
||||
20
Data.SeaHavenIndustries/Models/WorkOrderAuditLog.cs
Normal file
20
Data.SeaHavenIndustries/Models/WorkOrderAuditLog.cs
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
using System.ComponentModel.DataAnnotations.Schema;
|
||||
|
||||
namespace Data.SeaHavenIndustries
|
||||
{
|
||||
public class WorkOrderAuditLog
|
||||
{
|
||||
public int Id { get; set; }
|
||||
public int WorkOrderId { get; set; }
|
||||
[ForeignKey(nameof(WorkOrderId))]
|
||||
public virtual WorkOrder? WorkOrder { get; set; }
|
||||
public string? UserId { get; set; }
|
||||
[ForeignKey(nameof(UserId))]
|
||||
public virtual ApplicationUser? User { get; set; }
|
||||
public string? FieldName { get; set; }
|
||||
public string? OldValue { get; set; }
|
||||
public string? NewValue { get; set; }
|
||||
public string? Action { get; set; }
|
||||
public DateTime CreatedAt { get; set; }
|
||||
}
|
||||
}
|
||||
|
|
@ -6,9 +6,25 @@ namespace Data.SeaHavenIndustries
|
|||
{
|
||||
public class WorkOrder : FullAuditEntity
|
||||
{
|
||||
public string? InternalWONumber { get; set; }
|
||||
public string? ExternalWorkOrderId { get; set; }
|
||||
public string? WorkerOrderNumber { get; set; }
|
||||
public string? WorkerOrderTitle { get; set; }
|
||||
public string? Description { get; set; }
|
||||
public string? Customer { get; set; }
|
||||
public string? SiteCode { get; set; }
|
||||
public string? Building { get; set; }
|
||||
public string? Severity { get; set; }
|
||||
public DateTime? DateReported { get; set; }
|
||||
public DateTime? ScheduledStart { get; set; }
|
||||
public string? SourceEmailS3Key { get; set; }
|
||||
public string? Problem { get; set; }
|
||||
public string? Trade { get; set; }
|
||||
public string? SubTrade { get; set; }
|
||||
public decimal? VendorNTE { get; set; }
|
||||
public DateTime? ScheduledDate { get; set; }
|
||||
public DateTime? CompletedDate { get; set; }
|
||||
public string? Source { get; set; }
|
||||
public bool? istemplate { get; set; }
|
||||
public string? PO { get; set; }
|
||||
public string? TT { get; set; }
|
||||
|
|
@ -40,7 +56,6 @@ namespace Data.SeaHavenIndustries
|
|||
public ICollection<WorkOrderContacts>? WorkOrderContacts { get; set; }
|
||||
public ICollection<WorkOrderAttachments>? workOrderAttachments { get; set; }
|
||||
public ICollection<WorkOrderCategories>? WorkOrderCategories { get; set; }
|
||||
|
||||
|
||||
public ICollection<Dispatch>? Dispatches { get; set; }
|
||||
}
|
||||
}
|
||||
|
|
|
|||
109
FIX_LOCATIONS_SCHEMA.sql
Normal file
109
FIX_LOCATIONS_SCHEMA.sql
Normal file
|
|
@ -0,0 +1,109 @@
|
|||
-- 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
|
||||
100
SeaHaven.DataServices/Implementation/AccountDataService.cs
Normal file
100
SeaHaven.DataServices/Implementation/AccountDataService.cs
Normal file
|
|
@ -0,0 +1,100 @@
|
|||
using Data.SeaHavenIndustries;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using SeaHaven.DataServices.Interfaces;
|
||||
|
||||
namespace SeaHaven.DataServices.Implementation
|
||||
{
|
||||
public class AccountDataService : IAccountDataService
|
||||
{
|
||||
private readonly ApplicationDbContext _context;
|
||||
|
||||
public AccountDataService(ApplicationDbContext context)
|
||||
{
|
||||
_context = context;
|
||||
}
|
||||
|
||||
public async Task<Accounts?> GetByIdAsync(int id)
|
||||
{
|
||||
return await _context.Accounts.FindAsync(id);
|
||||
}
|
||||
|
||||
public async Task<Accounts?> GetByIdWithDetailsAsync(int id)
|
||||
{
|
||||
return await _context.Accounts
|
||||
.Include(a => a.Contacts)
|
||||
.Include(a => a.Addresses)
|
||||
.FirstOrDefaultAsync(a => a.Id == id);
|
||||
}
|
||||
|
||||
public async Task<IEnumerable<Accounts>> GetAllAsync()
|
||||
{
|
||||
return await _context.Accounts.ToListAsync();
|
||||
}
|
||||
|
||||
public async Task<(IEnumerable<Accounts> Items, int TotalCount)> GetPagedAsync(
|
||||
int page,
|
||||
int pageSize,
|
||||
string? search = null)
|
||||
{
|
||||
search ??= "";
|
||||
|
||||
var query = _context.Accounts
|
||||
.Where(a => a.IsDeleted == false || a.IsDeleted == null);
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(search))
|
||||
{
|
||||
var s = search.ToLower();
|
||||
query = query.Where(a =>
|
||||
(a.Name ?? "").ToLower().Contains(s) ||
|
||||
(a.Email ?? "").ToLower().Contains(s) ||
|
||||
(a.Phone ?? "").Contains(s) ||
|
||||
(a.Website ?? "").ToLower().Contains(s)
|
||||
);
|
||||
}
|
||||
|
||||
var totalCount = await query.CountAsync();
|
||||
var items = await query
|
||||
.OrderByDescending(a => a.Id)
|
||||
.Skip((page - 1) * pageSize)
|
||||
.Take(pageSize)
|
||||
.ToListAsync();
|
||||
|
||||
return (items, totalCount);
|
||||
}
|
||||
|
||||
public async Task<Accounts> AddAsync(Accounts account)
|
||||
{
|
||||
account.CreatedDate = DateTime.Now;
|
||||
await _context.Accounts.AddAsync(account);
|
||||
await _context.SaveChangesAsync();
|
||||
return account;
|
||||
}
|
||||
|
||||
public async Task UpdateAsync(Accounts account)
|
||||
{
|
||||
account.LastModificationTime = DateTime.Now;
|
||||
_context.Accounts.Update(account);
|
||||
await _context.SaveChangesAsync();
|
||||
}
|
||||
|
||||
public async Task DeleteAsync(int id)
|
||||
{
|
||||
var entity = await GetByIdAsync(id);
|
||||
if (entity != null)
|
||||
{
|
||||
_context.Accounts.Remove(entity);
|
||||
await _context.SaveChangesAsync();
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<bool> ExistsAsync(int id)
|
||||
{
|
||||
return await _context.Accounts.AnyAsync(a => a.Id == id);
|
||||
}
|
||||
|
||||
public async Task<int> CountAsync()
|
||||
{
|
||||
return await _context.Accounts.CountAsync();
|
||||
}
|
||||
}
|
||||
}
|
||||
108
SeaHaven.DataServices/Implementation/AssetDataService.cs
Normal file
108
SeaHaven.DataServices/Implementation/AssetDataService.cs
Normal file
|
|
@ -0,0 +1,108 @@
|
|||
using Data.SeaHavenIndustries;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using SeaHaven.DataServices.Interfaces;
|
||||
|
||||
namespace SeaHaven.DataServices.Implementation
|
||||
{
|
||||
public class AssetDataService : IAssetDataService
|
||||
{
|
||||
private readonly ApplicationDbContext _context;
|
||||
|
||||
public AssetDataService(ApplicationDbContext context)
|
||||
{
|
||||
_context = context;
|
||||
}
|
||||
|
||||
public async Task<Assets?> GetByIdAsync(int id)
|
||||
{
|
||||
return await _context.Assets.FindAsync(id);
|
||||
}
|
||||
|
||||
public async Task<Assets?> GetByIdWithDetailsAsync(int id)
|
||||
{
|
||||
return await _context.Assets
|
||||
.Include(a => a.Account)
|
||||
.Include(a => a.Location)
|
||||
.FirstOrDefaultAsync(a => a.Id == id);
|
||||
}
|
||||
|
||||
public async Task<IEnumerable<Assets>> GetAllAsync()
|
||||
{
|
||||
return await _context.Assets.ToListAsync();
|
||||
}
|
||||
|
||||
public async Task<IEnumerable<Assets>> GetByLocationIdAsync(int locationId)
|
||||
{
|
||||
return await _context.Assets
|
||||
.Where(a => a.LocationId == locationId)
|
||||
.ToListAsync();
|
||||
}
|
||||
|
||||
public async Task<(IEnumerable<Assets> Items, int TotalCount)> GetPagedAsync(
|
||||
int page,
|
||||
int pageSize,
|
||||
string? search = null)
|
||||
{
|
||||
search ??= "";
|
||||
|
||||
var query = _context.Assets
|
||||
.Include(a => a.Account)
|
||||
.Include(a => a.Location)
|
||||
.AsQueryable();
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(search))
|
||||
{
|
||||
var s = search.ToLower();
|
||||
query = query.Where(a =>
|
||||
(a.Name != null && a.Name.ToLower().Contains(s)) ||
|
||||
(a.AssetCode != null && a.AssetCode.ToLower().Contains(s)) ||
|
||||
(a.Description != null && a.Description.ToLower().Contains(s))
|
||||
);
|
||||
}
|
||||
|
||||
var totalCount = await query.CountAsync();
|
||||
var items = await query
|
||||
.OrderByDescending(a => a.Id)
|
||||
.Skip((page - 1) * pageSize)
|
||||
.Take(pageSize)
|
||||
.ToListAsync();
|
||||
|
||||
return (items, totalCount);
|
||||
}
|
||||
|
||||
public async Task<Assets> AddAsync(Assets asset)
|
||||
{
|
||||
asset.CreatedDate = DateTime.Now;
|
||||
await _context.Assets.AddAsync(asset);
|
||||
await _context.SaveChangesAsync();
|
||||
return asset;
|
||||
}
|
||||
|
||||
public async Task UpdateAsync(Assets asset)
|
||||
{
|
||||
asset.LastModificationTime = DateTime.Now;
|
||||
_context.Assets.Update(asset);
|
||||
await _context.SaveChangesAsync();
|
||||
}
|
||||
|
||||
public async Task DeleteAsync(int id)
|
||||
{
|
||||
var entity = await GetByIdAsync(id);
|
||||
if (entity != null)
|
||||
{
|
||||
_context.Assets.Remove(entity);
|
||||
await _context.SaveChangesAsync();
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<bool> ExistsAsync(int id)
|
||||
{
|
||||
return await _context.Assets.AnyAsync(a => a.Id == id);
|
||||
}
|
||||
|
||||
public async Task<int> CountAsync()
|
||||
{
|
||||
return await _context.Assets.CountAsync();
|
||||
}
|
||||
}
|
||||
}
|
||||
56
SeaHaven.DataServices/Implementation/CategoryDataService.cs
Normal file
56
SeaHaven.DataServices/Implementation/CategoryDataService.cs
Normal file
|
|
@ -0,0 +1,56 @@
|
|||
using Data.SeaHavenIndustries;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using SeaHaven.DataServices.Interfaces;
|
||||
|
||||
namespace SeaHaven.DataServices.Implementation
|
||||
{
|
||||
public class CategoryDataService : ICategoryDataService
|
||||
{
|
||||
private readonly ApplicationDbContext _context;
|
||||
|
||||
public CategoryDataService(ApplicationDbContext context)
|
||||
{
|
||||
_context = context;
|
||||
}
|
||||
|
||||
public async Task<Category?> GetByIdAsync(int id)
|
||||
{
|
||||
return await _context.Categories.FindAsync(id);
|
||||
}
|
||||
|
||||
public async Task<IEnumerable<Category>> GetAllAsync()
|
||||
{
|
||||
return await _context.Categories.ToListAsync();
|
||||
}
|
||||
|
||||
public async Task<Category> AddAsync(Category category)
|
||||
{
|
||||
category.CreatedDate = DateTime.Now;
|
||||
await _context.Categories.AddAsync(category);
|
||||
await _context.SaveChangesAsync();
|
||||
return category;
|
||||
}
|
||||
|
||||
public async Task UpdateAsync(Category category)
|
||||
{
|
||||
category.LastModificationTime = DateTime.Now;
|
||||
_context.Categories.Update(category);
|
||||
await _context.SaveChangesAsync();
|
||||
}
|
||||
|
||||
public async Task DeleteAsync(int id)
|
||||
{
|
||||
var entity = await GetByIdAsync(id);
|
||||
if (entity != null)
|
||||
{
|
||||
_context.Categories.Remove(entity);
|
||||
await _context.SaveChangesAsync();
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<bool> ExistsAsync(int id)
|
||||
{
|
||||
return await _context.Categories.AnyAsync(c => c.Id == id);
|
||||
}
|
||||
}
|
||||
}
|
||||
160
SeaHaven.DataServices/Implementation/ContactDataService.cs
Normal file
160
SeaHaven.DataServices/Implementation/ContactDataService.cs
Normal file
|
|
@ -0,0 +1,160 @@
|
|||
using Data.SeaHavenIndustries;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using SeaHaven.DataServices.Interfaces;
|
||||
|
||||
namespace SeaHaven.DataServices.Implementation
|
||||
{
|
||||
public class ContactDataService : IContactDataService
|
||||
{
|
||||
private readonly ApplicationDbContext _context;
|
||||
|
||||
public ContactDataService(ApplicationDbContext context)
|
||||
{
|
||||
_context = context;
|
||||
}
|
||||
|
||||
public async Task<Contacts?> GetByIdAsync(int id)
|
||||
{
|
||||
return await _context.Contacts.FindAsync(id);
|
||||
}
|
||||
|
||||
public async Task<Contacts?> GetByIdWithDetailsAsync(int id)
|
||||
{
|
||||
return await _context.Contacts
|
||||
.Include(c => c.Location)
|
||||
.Include(c => c.Account)
|
||||
.FirstOrDefaultAsync(c => c.Id == id);
|
||||
}
|
||||
|
||||
public async Task<IEnumerable<Contacts>> GetAllAsync()
|
||||
{
|
||||
return await _context.Contacts.ToListAsync();
|
||||
}
|
||||
|
||||
public async Task<IEnumerable<Contacts>> GetByLocationIdAsync(int locationId)
|
||||
{
|
||||
return await _context.Contacts
|
||||
.Where(c => c.LocationId == locationId)
|
||||
.ToListAsync();
|
||||
}
|
||||
|
||||
public async Task<IEnumerable<Contacts>> GetByAccountIdAsync(int accountId)
|
||||
{
|
||||
return await _context.Contacts
|
||||
.Where(c => c.AccountId == accountId)
|
||||
.ToListAsync();
|
||||
}
|
||||
|
||||
public async Task<IEnumerable<Contacts>> GetByTypeAsync(string type)
|
||||
{
|
||||
return await _context.Contacts
|
||||
.Where(c => c.ContactType == type)
|
||||
.ToListAsync();
|
||||
}
|
||||
|
||||
public async Task<Contacts?> GetByEmailAsync(string email)
|
||||
{
|
||||
return await _context.Contacts
|
||||
.FirstOrDefaultAsync(c => c.Email == email);
|
||||
}
|
||||
|
||||
public async Task<Contacts?> GetByPhoneAsync(string phone)
|
||||
{
|
||||
return await _context.Contacts
|
||||
.FirstOrDefaultAsync(c => c.PhoneNumber == phone);
|
||||
}
|
||||
|
||||
public async Task<(IEnumerable<Contacts> Items, int TotalCount)> GetPagedAsync(
|
||||
int page,
|
||||
int pageSize,
|
||||
string? search = null,
|
||||
string? type = null)
|
||||
{
|
||||
search ??= "";
|
||||
var query = _context.Contacts.AsQueryable();
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(search))
|
||||
{
|
||||
var s = search.ToLower();
|
||||
query = query.Where(c =>
|
||||
(c.FirstName != null && c.FirstName.ToLower().Contains(s)) ||
|
||||
(c.LastName != null && c.LastName.ToLower().Contains(s)) ||
|
||||
(c.Email != null && c.Email.ToLower().Contains(s)) ||
|
||||
(c.PhoneNumber != null && c.PhoneNumber.Contains(s)));
|
||||
}
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(type))
|
||||
{
|
||||
query = query.Where(c => c.ContactType == type);
|
||||
}
|
||||
|
||||
var totalCount = await query.CountAsync();
|
||||
var items = await query
|
||||
.OrderByDescending(c => c.Id)
|
||||
.Skip((page - 1) * pageSize)
|
||||
.Take(pageSize)
|
||||
.ToListAsync();
|
||||
|
||||
return (items, totalCount);
|
||||
}
|
||||
|
||||
public async Task<Contacts> AddAsync(Contacts contact)
|
||||
{
|
||||
contact.CreatedDate = DateTime.Now;
|
||||
await _context.Contacts.AddAsync(contact);
|
||||
await _context.SaveChangesAsync();
|
||||
return contact;
|
||||
}
|
||||
|
||||
public async Task UpdateAsync(Contacts contact)
|
||||
{
|
||||
contact.LastModificationTime = DateTime.Now;
|
||||
_context.Contacts.Update(contact);
|
||||
await _context.SaveChangesAsync();
|
||||
}
|
||||
|
||||
public async Task DeleteAsync(int id)
|
||||
{
|
||||
var entity = await GetByIdAsync(id);
|
||||
if (entity != null)
|
||||
{
|
||||
_context.Contacts.Remove(entity);
|
||||
await _context.SaveChangesAsync();
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<bool> ExistsAsync(int id)
|
||||
{
|
||||
return await _context.Contacts.AnyAsync(c => c.Id == id);
|
||||
}
|
||||
|
||||
public async Task<bool> EmailExistsAsync(string email, int? excludeId = null)
|
||||
{
|
||||
return await _context.Contacts.AnyAsync(c =>
|
||||
c.Email == email &&
|
||||
(excludeId == null || c.Id != excludeId));
|
||||
}
|
||||
|
||||
public async Task<bool> PhoneExistsAsync(string phone, int? excludeId = null)
|
||||
{
|
||||
return await _context.Contacts.AnyAsync(c =>
|
||||
c.PhoneNumber == phone &&
|
||||
(excludeId == null || c.Id != excludeId));
|
||||
}
|
||||
|
||||
public async Task<int> CountAsync()
|
||||
{
|
||||
return await _context.Contacts.CountAsync();
|
||||
}
|
||||
|
||||
public async Task<int> CountByAccountIdAsync(int accountId)
|
||||
{
|
||||
return await _context.Contacts.CountAsync(c => c.AccountId == accountId);
|
||||
}
|
||||
|
||||
public async Task<int> CountByTypeAsync(string type)
|
||||
{
|
||||
return await _context.Contacts.CountAsync(c => c.ContactType == type);
|
||||
}
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Reference in a new issue