first commit
This commit is contained in:
510
Logic/LoginLogic.cs
Normal file
510
Logic/LoginLogic.cs
Normal file
@@ -0,0 +1,510 @@
|
||||
using MotoVaultPro.External.Interfaces;
|
||||
using MotoVaultPro.Helper;
|
||||
using MotoVaultPro.Models;
|
||||
using Microsoft.Extensions.Caching.Memory;
|
||||
using Microsoft.IdentityModel.Tokens;
|
||||
using System.Security.Cryptography;
|
||||
using System.Text;
|
||||
using System.Text.Json;
|
||||
|
||||
namespace MotoVaultPro.Logic
|
||||
{
|
||||
public interface ILoginLogic
|
||||
{
|
||||
bool MakeUserAdmin(int userId, bool isAdmin);
|
||||
OperationResponse GenerateUserToken(string emailAddress, bool autoNotify);
|
||||
bool DeleteUserToken(int tokenId);
|
||||
bool DeleteUser(int userId);
|
||||
OperationResponse RegisterOpenIdUser(LoginModel credentials);
|
||||
OperationResponse UpdateUserDetails(int userId, LoginModel credentials);
|
||||
OperationResponse RegisterNewUser(LoginModel credentials);
|
||||
OperationResponse RequestResetPassword(LoginModel credentials);
|
||||
OperationResponse ResetPasswordByUser(LoginModel credentials);
|
||||
OperationResponse ResetUserPassword(LoginModel credentials);
|
||||
OperationResponse SendRegistrationToken(LoginModel credentials);
|
||||
UserData ValidateUserCredentials(LoginModel credentials);
|
||||
UserData ValidateOpenIDUser(LoginModel credentials);
|
||||
bool CheckIfUserIsValid(int userId);
|
||||
bool CreateRootUserCredentials(LoginModel credentials);
|
||||
bool DeleteRootUserCredentials();
|
||||
bool GenerateTokenForEmailAddress(string emailAddress, bool isPasswordReset);
|
||||
List<UserData> GetAllUsers();
|
||||
List<Token> GetAllTokens();
|
||||
KeyValuePair<string, string> GetPKCEChallengeCode();
|
||||
}
|
||||
public class LoginLogic : ILoginLogic
|
||||
{
|
||||
private readonly IUserRecordDataAccess _userData;
|
||||
private readonly ITokenRecordDataAccess _tokenData;
|
||||
private readonly IMailHelper _mailHelper;
|
||||
private readonly IConfigHelper _configHelper;
|
||||
private IMemoryCache _cache;
|
||||
public LoginLogic(IUserRecordDataAccess userData,
|
||||
ITokenRecordDataAccess tokenData,
|
||||
IMailHelper mailHelper,
|
||||
IConfigHelper configHelper,
|
||||
IMemoryCache memoryCache)
|
||||
{
|
||||
_userData = userData;
|
||||
_tokenData = tokenData;
|
||||
_mailHelper = mailHelper;
|
||||
_configHelper = configHelper;
|
||||
_cache = memoryCache;
|
||||
}
|
||||
public bool CheckIfUserIsValid(int userId)
|
||||
{
|
||||
if (userId == -1)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
var result = _userData.GetUserRecordById(userId);
|
||||
if (result == null)
|
||||
{
|
||||
return false;
|
||||
} else
|
||||
{
|
||||
return result.Id != 0;
|
||||
}
|
||||
}
|
||||
public OperationResponse UpdateUserDetails(int userId, LoginModel credentials)
|
||||
{
|
||||
//get current user details
|
||||
var existingUser = _userData.GetUserRecordById(userId);
|
||||
if (existingUser.Id == default)
|
||||
{
|
||||
return OperationResponse.Failed("Invalid user");
|
||||
}
|
||||
//validate user token
|
||||
var existingToken = _tokenData.GetTokenRecordByBody(credentials.Token);
|
||||
if (existingToken.Id == default || existingToken.EmailAddress != existingUser.EmailAddress)
|
||||
{
|
||||
return OperationResponse.Failed("Invalid Token");
|
||||
}
|
||||
if (!string.IsNullOrWhiteSpace(credentials.UserName) && existingUser.UserName != credentials.UserName)
|
||||
{
|
||||
//check if new username is already taken.
|
||||
var existingUserWithUserName = _userData.GetUserRecordByUserName(credentials.UserName);
|
||||
if (existingUserWithUserName.Id != default)
|
||||
{
|
||||
return OperationResponse.Failed("Username already taken");
|
||||
}
|
||||
existingUser.UserName = credentials.UserName;
|
||||
}
|
||||
if (!string.IsNullOrWhiteSpace(credentials.EmailAddress) && existingUser.EmailAddress != credentials.EmailAddress)
|
||||
{
|
||||
//check if email address already exists
|
||||
var existingUserWithEmailAddress = _userData.GetUserRecordByEmailAddress(credentials.EmailAddress);
|
||||
if (existingUserWithEmailAddress.Id != default)
|
||||
{
|
||||
return OperationResponse.Failed("A user with that email already exists");
|
||||
}
|
||||
existingUser.EmailAddress = credentials.EmailAddress;
|
||||
}
|
||||
if (!string.IsNullOrWhiteSpace(credentials.Password))
|
||||
{
|
||||
//update password
|
||||
existingUser.Password = GetHash(credentials.Password);
|
||||
}
|
||||
//delete token
|
||||
_tokenData.DeleteToken(existingToken.Id);
|
||||
var result = _userData.SaveUserRecord(existingUser);
|
||||
return OperationResponse.Conditional(result, "User Updated", string.Empty);
|
||||
}
|
||||
public OperationResponse RegisterOpenIdUser(LoginModel credentials)
|
||||
{
|
||||
//validate their token.
|
||||
var existingToken = _tokenData.GetTokenRecordByBody(credentials.Token);
|
||||
if (existingToken.Id == default || existingToken.EmailAddress != credentials.EmailAddress)
|
||||
{
|
||||
return OperationResponse.Failed("Invalid Token");
|
||||
}
|
||||
if (string.IsNullOrWhiteSpace(credentials.EmailAddress) || string.IsNullOrWhiteSpace(credentials.UserName))
|
||||
{
|
||||
return OperationResponse.Failed("Username cannot be blank");
|
||||
}
|
||||
var existingUser = _userData.GetUserRecordByUserName(credentials.UserName);
|
||||
if (existingUser.Id != default)
|
||||
{
|
||||
return OperationResponse.Failed("Username already taken");
|
||||
}
|
||||
var existingUserWithEmail = _userData.GetUserRecordByEmailAddress(credentials.EmailAddress);
|
||||
if (existingUserWithEmail.Id != default)
|
||||
{
|
||||
return OperationResponse.Failed("A user with that email already exists");
|
||||
}
|
||||
_tokenData.DeleteToken(existingToken.Id);
|
||||
var newUser = new UserData()
|
||||
{
|
||||
UserName = credentials.UserName,
|
||||
Password = GetHash(NewToken()), //generate a password for OpenID User
|
||||
EmailAddress = credentials.EmailAddress
|
||||
};
|
||||
var result = _userData.SaveUserRecord(newUser);
|
||||
if (result)
|
||||
{
|
||||
return OperationResponse.Succeed("You will be logged in briefly.");
|
||||
}
|
||||
else
|
||||
{
|
||||
return OperationResponse.Failed("Something went wrong, please try again later.");
|
||||
}
|
||||
}
|
||||
//handles user registration
|
||||
public OperationResponse RegisterNewUser(LoginModel credentials)
|
||||
{
|
||||
//validate their token.
|
||||
var existingToken = _tokenData.GetTokenRecordByBody(credentials.Token);
|
||||
if (existingToken.Id == default || existingToken.EmailAddress != credentials.EmailAddress)
|
||||
{
|
||||
return OperationResponse.Failed("Invalid Token");
|
||||
}
|
||||
//token is valid, check if username and password is acceptable and that username is unique.
|
||||
if (string.IsNullOrWhiteSpace(credentials.EmailAddress) || string.IsNullOrWhiteSpace(credentials.UserName) || string.IsNullOrWhiteSpace(credentials.Password))
|
||||
{
|
||||
return OperationResponse.Failed("Neither username nor password can be blank");
|
||||
}
|
||||
var existingUser = _userData.GetUserRecordByUserName(credentials.UserName);
|
||||
if (existingUser.Id != default)
|
||||
{
|
||||
return OperationResponse.Failed("Username already taken");
|
||||
}
|
||||
var existingUserWithEmail = _userData.GetUserRecordByEmailAddress(credentials.EmailAddress);
|
||||
if (existingUserWithEmail.Id != default)
|
||||
{
|
||||
return OperationResponse.Failed("A user with that email already exists");
|
||||
}
|
||||
//username is unique then we delete the token and create the user.
|
||||
_tokenData.DeleteToken(existingToken.Id);
|
||||
var newUser = new UserData()
|
||||
{
|
||||
UserName = credentials.UserName,
|
||||
Password = GetHash(credentials.Password),
|
||||
EmailAddress = credentials.EmailAddress
|
||||
};
|
||||
var result = _userData.SaveUserRecord(newUser);
|
||||
if (result)
|
||||
{
|
||||
return OperationResponse.Succeed("You will be redirected to the login page briefly.");
|
||||
}
|
||||
else
|
||||
{
|
||||
return OperationResponse.Failed();
|
||||
}
|
||||
}
|
||||
public OperationResponse SendRegistrationToken(LoginModel credentials)
|
||||
{
|
||||
if (_configHelper.GetServerOpenRegistration())
|
||||
{
|
||||
return GenerateUserToken(credentials.EmailAddress, true);
|
||||
} else
|
||||
{
|
||||
return OperationResponse.Failed("Open Registration Disabled");
|
||||
}
|
||||
}
|
||||
/// <summary>
|
||||
/// Generates a token and notifies user via email so they can reset their password.
|
||||
/// </summary>
|
||||
/// <param name="credentials"></param>
|
||||
/// <returns></returns>
|
||||
public OperationResponse RequestResetPassword(LoginModel credentials)
|
||||
{
|
||||
var existingUser = _userData.GetUserRecordByUserName(credentials.UserName);
|
||||
if (existingUser.Id != default)
|
||||
{
|
||||
//user exists, generate a token and send email.
|
||||
GenerateTokenForEmailAddress(existingUser.EmailAddress, true);
|
||||
}
|
||||
//for security purposes we want to always return true for this method.
|
||||
//otherwise someone can spam the reset password method to sniff out users.
|
||||
return OperationResponse.Succeed("If your user exists in the system you should receive an email shortly with instructions on how to proceed.");
|
||||
}
|
||||
public OperationResponse ResetPasswordByUser(LoginModel credentials)
|
||||
{
|
||||
var existingToken = _tokenData.GetTokenRecordByBody(credentials.Token);
|
||||
if (existingToken.Id == default || existingToken.EmailAddress != credentials.EmailAddress)
|
||||
{
|
||||
return OperationResponse.Failed("Invalid Token");
|
||||
}
|
||||
if (string.IsNullOrWhiteSpace(credentials.Password))
|
||||
{
|
||||
return OperationResponse.Failed("New Password cannot be blank");
|
||||
}
|
||||
//if token is valid.
|
||||
var existingUser = _userData.GetUserRecordByEmailAddress(credentials.EmailAddress);
|
||||
if (existingUser.Id == default)
|
||||
{
|
||||
return OperationResponse.Failed("Unable to locate user");
|
||||
}
|
||||
existingUser.Password = GetHash(credentials.Password);
|
||||
var result = _userData.SaveUserRecord(existingUser);
|
||||
//delete token
|
||||
_tokenData.DeleteToken(existingToken.Id);
|
||||
if (result)
|
||||
{
|
||||
return OperationResponse.Succeed("Password resetted, you will be redirected to login page shortly.");
|
||||
} else
|
||||
{
|
||||
return OperationResponse.Failed();
|
||||
}
|
||||
}
|
||||
/// <summary>
|
||||
/// Returns an empty user if can't auth against neither root nor db user.
|
||||
/// </summary>
|
||||
/// <param name="credentials">credentials from login page</param>
|
||||
/// <returns></returns>
|
||||
public UserData ValidateUserCredentials(LoginModel credentials)
|
||||
{
|
||||
if (UserIsRoot(credentials))
|
||||
{
|
||||
return GetRootUserData(credentials.UserName);
|
||||
}
|
||||
else
|
||||
{
|
||||
//authenticate via DB.
|
||||
var result = _userData.GetUserRecordByUserName(credentials.UserName);
|
||||
if (GetHash(credentials.Password) == result.Password)
|
||||
{
|
||||
result.Password = string.Empty;
|
||||
return result;
|
||||
}
|
||||
else
|
||||
{
|
||||
return new UserData();
|
||||
}
|
||||
}
|
||||
}
|
||||
public UserData ValidateOpenIDUser(LoginModel credentials)
|
||||
{
|
||||
//validate for root user
|
||||
var isRootUser = _configHelper.AuthenticateRootUserOIDC(credentials.EmailAddress);
|
||||
if (isRootUser)
|
||||
{
|
||||
return GetRootUserData(credentials.EmailAddress);
|
||||
}
|
||||
|
||||
var result = _userData.GetUserRecordByEmailAddress(credentials.EmailAddress);
|
||||
if (result.Id != default)
|
||||
{
|
||||
result.Password = string.Empty;
|
||||
return result;
|
||||
}
|
||||
else
|
||||
{
|
||||
return new UserData();
|
||||
}
|
||||
}
|
||||
#region "Admin Functions"
|
||||
public bool MakeUserAdmin(int userId, bool isAdmin)
|
||||
{
|
||||
var user = _userData.GetUserRecordById(userId);
|
||||
if (user == default)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
user.IsAdmin = isAdmin;
|
||||
var result = _userData.SaveUserRecord(user);
|
||||
return result;
|
||||
}
|
||||
public List<UserData> GetAllUsers()
|
||||
{
|
||||
var result = _userData.GetUsers();
|
||||
return result;
|
||||
}
|
||||
public List<Token> GetAllTokens()
|
||||
{
|
||||
var result = _tokenData.GetTokens();
|
||||
return result;
|
||||
}
|
||||
public OperationResponse GenerateUserToken(string emailAddress, bool autoNotify)
|
||||
{
|
||||
//check if email address already has a token attached to it.
|
||||
var existingToken = _tokenData.GetTokenRecordByEmailAddress(emailAddress);
|
||||
if (existingToken.Id != default)
|
||||
{
|
||||
if (autoNotify) //re-send email
|
||||
{
|
||||
var notificationResult = _mailHelper.NotifyUserForRegistration(emailAddress, existingToken.Body);
|
||||
if (notificationResult.Success)
|
||||
{
|
||||
return OperationResponse.Failed($"There is an existing token tied to {emailAddress}, a new email has been sent out");
|
||||
} else
|
||||
{
|
||||
return notificationResult;
|
||||
}
|
||||
}
|
||||
return OperationResponse.Failed($"There is an existing token tied to {emailAddress}");
|
||||
}
|
||||
var token = new Token()
|
||||
{
|
||||
Body = NewToken(),
|
||||
EmailAddress = emailAddress
|
||||
};
|
||||
var result = _tokenData.CreateNewToken(token);
|
||||
if (result && autoNotify)
|
||||
{
|
||||
result = _mailHelper.NotifyUserForRegistration(emailAddress, token.Body).Success;
|
||||
if (!result)
|
||||
{
|
||||
return OperationResponse.Failed("Token Generated, but Email failed to send, please check your SMTP settings.");
|
||||
}
|
||||
}
|
||||
if (result)
|
||||
{
|
||||
return OperationResponse.Succeed("Token Generated!");
|
||||
}
|
||||
else
|
||||
{
|
||||
return OperationResponse.Failed();
|
||||
}
|
||||
}
|
||||
public bool DeleteUserToken(int tokenId)
|
||||
{
|
||||
var result = _tokenData.DeleteToken(tokenId);
|
||||
return result;
|
||||
}
|
||||
public bool DeleteUser(int userId)
|
||||
{
|
||||
var result = _userData.DeleteUserRecord(userId);
|
||||
return result;
|
||||
}
|
||||
public OperationResponse ResetUserPassword(LoginModel credentials)
|
||||
{
|
||||
//user might have forgotten their password.
|
||||
var existingUser = _userData.GetUserRecordByUserName(credentials.UserName);
|
||||
if (existingUser.Id == default)
|
||||
{
|
||||
return OperationResponse.Failed("Unable to find user");
|
||||
}
|
||||
var newPassword = Guid.NewGuid().ToString().Substring(0, 8);
|
||||
existingUser.Password = GetHash(newPassword);
|
||||
var result = _userData.SaveUserRecord(existingUser);
|
||||
if (result)
|
||||
{
|
||||
return OperationResponse.Succeed(newPassword);
|
||||
}
|
||||
else
|
||||
{
|
||||
return OperationResponse.Failed();
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
#region "Root User"
|
||||
public bool CreateRootUserCredentials(LoginModel credentials)
|
||||
{
|
||||
//check if file exists
|
||||
if (File.Exists(StaticHelper.UserConfigPath))
|
||||
{
|
||||
var configFileContents = File.ReadAllText(StaticHelper.UserConfigPath);
|
||||
var existingUserConfig = JsonSerializer.Deserialize<UserConfig>(configFileContents);
|
||||
if (existingUserConfig is not null)
|
||||
{
|
||||
//create hashes of the login credentials.
|
||||
var hashedUserName = GetHash(credentials.UserName);
|
||||
var hashedPassword = GetHash(credentials.Password);
|
||||
//copy over settings that are off limits on the settings page.
|
||||
existingUserConfig.EnableAuth = true;
|
||||
existingUserConfig.UserNameHash = hashedUserName;
|
||||
existingUserConfig.UserPasswordHash = hashedPassword;
|
||||
}
|
||||
File.WriteAllText(StaticHelper.UserConfigPath, JsonSerializer.Serialize(existingUserConfig));
|
||||
} else
|
||||
{
|
||||
var newUserConfig = new UserConfig()
|
||||
{
|
||||
EnableAuth = true,
|
||||
UserNameHash = GetHash(credentials.UserName),
|
||||
UserPasswordHash = GetHash(credentials.Password)
|
||||
};
|
||||
File.WriteAllText(StaticHelper.UserConfigPath, JsonSerializer.Serialize(newUserConfig));
|
||||
}
|
||||
_cache.Remove("userConfig_-1");
|
||||
return true;
|
||||
}
|
||||
public bool DeleteRootUserCredentials()
|
||||
{
|
||||
var configFileContents = File.ReadAllText(StaticHelper.UserConfigPath);
|
||||
var existingUserConfig = JsonSerializer.Deserialize<UserConfig>(configFileContents);
|
||||
if (existingUserConfig is not null)
|
||||
{
|
||||
//copy over settings that are off limits on the settings page.
|
||||
existingUserConfig.EnableAuth = false;
|
||||
existingUserConfig.UserNameHash = string.Empty;
|
||||
existingUserConfig.UserPasswordHash = string.Empty;
|
||||
}
|
||||
//clear out the cached config for the root user.
|
||||
_cache.Remove("userConfig_-1");
|
||||
File.WriteAllText(StaticHelper.UserConfigPath, JsonSerializer.Serialize(existingUserConfig));
|
||||
return true;
|
||||
}
|
||||
private bool UserIsRoot(LoginModel credentials)
|
||||
{
|
||||
var hashedUserName = GetHash(credentials.UserName);
|
||||
var hashedPassword = GetHash(credentials.Password);
|
||||
return _configHelper.AuthenticateRootUser(hashedUserName, hashedPassword);
|
||||
}
|
||||
private UserData GetRootUserData(string username)
|
||||
{
|
||||
return new UserData()
|
||||
{
|
||||
Id = -1,
|
||||
UserName = username,
|
||||
IsAdmin = true,
|
||||
IsRootUser = true,
|
||||
EmailAddress = string.Empty
|
||||
};
|
||||
}
|
||||
#endregion
|
||||
private static string GetHash(string value)
|
||||
{
|
||||
StringBuilder Sb = new StringBuilder();
|
||||
|
||||
using (var hash = SHA256.Create())
|
||||
{
|
||||
Encoding enc = Encoding.UTF8;
|
||||
byte[] result = hash.ComputeHash(enc.GetBytes(value));
|
||||
|
||||
foreach (byte b in result)
|
||||
Sb.Append(b.ToString("x2"));
|
||||
}
|
||||
|
||||
return Sb.ToString();
|
||||
}
|
||||
private string NewToken()
|
||||
{
|
||||
return Guid.NewGuid().ToString().Substring(0, 8);
|
||||
}
|
||||
public KeyValuePair<string, string> GetPKCEChallengeCode()
|
||||
{
|
||||
var verifierCode = Base64UrlEncoder.Encode(Guid.NewGuid().ToString().Replace("-", ""));
|
||||
var verifierBytes = Encoding.UTF8.GetBytes(verifierCode);
|
||||
var hashedCode = SHA256.Create().ComputeHash(verifierBytes);
|
||||
var encodedChallengeCode = Base64UrlEncoder.Encode(hashedCode);
|
||||
return new KeyValuePair<string, string>(verifierCode, encodedChallengeCode);
|
||||
}
|
||||
public bool GenerateTokenForEmailAddress(string emailAddress, bool isPasswordReset)
|
||||
{
|
||||
bool result = false;
|
||||
//check if there is already a token tied to this email address.
|
||||
var existingToken = _tokenData.GetTokenRecordByEmailAddress(emailAddress);
|
||||
if (existingToken.Id == default)
|
||||
{
|
||||
//no token, generate one and send.
|
||||
var token = new Token()
|
||||
{
|
||||
Body = NewToken(),
|
||||
EmailAddress = emailAddress
|
||||
};
|
||||
result = _tokenData.CreateNewToken(token);
|
||||
if (result)
|
||||
{
|
||||
result = isPasswordReset ? _mailHelper.NotifyUserForPasswordReset(emailAddress, token.Body).Success : _mailHelper.NotifyUserForAccountUpdate(emailAddress, token.Body).Success;
|
||||
}
|
||||
} else
|
||||
{
|
||||
//token exists, send it again.
|
||||
result = isPasswordReset ? _mailHelper.NotifyUserForPasswordReset(emailAddress, existingToken.Body).Success : _mailHelper.NotifyUserForAccountUpdate(emailAddress, existingToken.Body).Success;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
71
Logic/OdometerLogic.cs
Normal file
71
Logic/OdometerLogic.cs
Normal file
@@ -0,0 +1,71 @@
|
||||
using MotoVaultPro.External.Interfaces;
|
||||
using MotoVaultPro.Models;
|
||||
|
||||
namespace MotoVaultPro.Logic
|
||||
{
|
||||
public interface IOdometerLogic
|
||||
{
|
||||
int GetLastOdometerRecordMileage(int vehicleId, List<OdometerRecord> odometerRecords);
|
||||
bool AutoInsertOdometerRecord(OdometerRecord odometer);
|
||||
List<OdometerRecord> AutoConvertOdometerRecord(List<OdometerRecord> odometerRecords);
|
||||
}
|
||||
public class OdometerLogic: IOdometerLogic
|
||||
{
|
||||
private readonly IOdometerRecordDataAccess _odometerRecordDataAccess;
|
||||
private readonly ILogger<IOdometerLogic> _logger;
|
||||
public OdometerLogic(IOdometerRecordDataAccess odometerRecordDataAccess, ILogger<IOdometerLogic> logger)
|
||||
{
|
||||
_odometerRecordDataAccess = odometerRecordDataAccess;
|
||||
_logger = logger;
|
||||
}
|
||||
public int GetLastOdometerRecordMileage(int vehicleId, List<OdometerRecord> odometerRecords)
|
||||
{
|
||||
if (!odometerRecords.Any())
|
||||
{
|
||||
odometerRecords = _odometerRecordDataAccess.GetOdometerRecordsByVehicleId(vehicleId);
|
||||
}
|
||||
if (!odometerRecords.Any())
|
||||
{
|
||||
//no existing odometer records for this vehicle.
|
||||
return 0;
|
||||
}
|
||||
return odometerRecords.Max(x => x.Mileage);
|
||||
}
|
||||
public bool AutoInsertOdometerRecord(OdometerRecord odometer)
|
||||
{
|
||||
if (odometer.Mileage == default)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
var lastReportedMileage = GetLastOdometerRecordMileage(odometer.VehicleId, new List<OdometerRecord>());
|
||||
odometer.InitialMileage = lastReportedMileage != default ? lastReportedMileage : odometer.Mileage;
|
||||
|
||||
var result = _odometerRecordDataAccess.SaveOdometerRecordToVehicle(odometer);
|
||||
return result;
|
||||
}
|
||||
public List<OdometerRecord> AutoConvertOdometerRecord(List<OdometerRecord> odometerRecords)
|
||||
{
|
||||
//perform ordering
|
||||
odometerRecords = odometerRecords.OrderBy(x => x.Date).ThenBy(x => x.Mileage).ToList();
|
||||
int previousMileage = 0;
|
||||
for (int i = 0; i < odometerRecords.Count; i++)
|
||||
{
|
||||
var currentObject = odometerRecords[i];
|
||||
if (previousMileage == default)
|
||||
{
|
||||
//first record
|
||||
currentObject.InitialMileage = currentObject.Mileage;
|
||||
}
|
||||
else
|
||||
{
|
||||
//subsequent records
|
||||
currentObject.InitialMileage = previousMileage;
|
||||
}
|
||||
//save to db.
|
||||
_odometerRecordDataAccess.SaveOdometerRecordToVehicle(currentObject);
|
||||
previousMileage = currentObject.Mileage;
|
||||
}
|
||||
return odometerRecords;
|
||||
}
|
||||
}
|
||||
}
|
||||
123
Logic/UserLogic.cs
Normal file
123
Logic/UserLogic.cs
Normal file
@@ -0,0 +1,123 @@
|
||||
using MotoVaultPro.External.Interfaces;
|
||||
using MotoVaultPro.Helper;
|
||||
using MotoVaultPro.Models;
|
||||
|
||||
namespace MotoVaultPro.Logic
|
||||
{
|
||||
public interface IUserLogic
|
||||
{
|
||||
List<UserCollaborator> GetCollaboratorsForVehicle(int vehicleId);
|
||||
bool AddUserAccessToVehicle(int userId, int vehicleId);
|
||||
bool DeleteCollaboratorFromVehicle(int userId, int vehicleId);
|
||||
OperationResponse AddCollaboratorToVehicle(int vehicleId, string username);
|
||||
List<Vehicle> FilterUserVehicles(List<Vehicle> results, int userId);
|
||||
bool UserCanEditVehicle(int userId, int vehicleId);
|
||||
bool DeleteAllAccessToVehicle(int vehicleId);
|
||||
bool DeleteAllAccessToUser(int userId);
|
||||
}
|
||||
public class UserLogic: IUserLogic
|
||||
{
|
||||
private readonly IUserAccessDataAccess _userAccess;
|
||||
private readonly IUserRecordDataAccess _userData;
|
||||
public UserLogic(IUserAccessDataAccess userAccess,
|
||||
IUserRecordDataAccess userData) {
|
||||
_userAccess = userAccess;
|
||||
_userData = userData;
|
||||
}
|
||||
public List<UserCollaborator> GetCollaboratorsForVehicle(int vehicleId)
|
||||
{
|
||||
var result = _userAccess.GetUserAccessByVehicleId(vehicleId);
|
||||
var convertedResult = new List<UserCollaborator>();
|
||||
//convert useraccess to usercollaborator
|
||||
foreach(UserAccess userAccess in result)
|
||||
{
|
||||
var userCollaborator = new UserCollaborator
|
||||
{
|
||||
UserName = _userData.GetUserRecordById(userAccess.Id.UserId).UserName,
|
||||
UserVehicle = userAccess.Id
|
||||
};
|
||||
convertedResult.Add(userCollaborator);
|
||||
}
|
||||
return convertedResult;
|
||||
}
|
||||
public OperationResponse AddCollaboratorToVehicle(int vehicleId, string username)
|
||||
{
|
||||
//try to find existing user.
|
||||
var existingUser = _userData.GetUserRecordByUserName(username);
|
||||
if (existingUser.Id != default)
|
||||
{
|
||||
//user exists.
|
||||
//check if user is already a collaborator
|
||||
var userAccess = _userAccess.GetUserAccessByVehicleAndUserId(existingUser.Id, vehicleId);
|
||||
if (userAccess != null)
|
||||
{
|
||||
return OperationResponse.Failed("User is already a collaborator");
|
||||
}
|
||||
var result = AddUserAccessToVehicle(existingUser.Id, vehicleId);
|
||||
if (result)
|
||||
{
|
||||
return OperationResponse.Succeed("Collaborator Added");
|
||||
}
|
||||
return OperationResponse.Failed();
|
||||
}
|
||||
return OperationResponse.Failed($"Unable to find user {username} in the system");
|
||||
}
|
||||
public bool DeleteCollaboratorFromVehicle(int userId, int vehicleId)
|
||||
{
|
||||
var result = _userAccess.DeleteUserAccess(userId, vehicleId);
|
||||
return result;
|
||||
}
|
||||
public bool AddUserAccessToVehicle(int userId, int vehicleId)
|
||||
{
|
||||
if (userId == -1)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
var userVehicle = new UserVehicle { UserId = userId, VehicleId = vehicleId };
|
||||
var userAccess = new UserAccess { Id = userVehicle };
|
||||
var result = _userAccess.SaveUserAccess(userAccess);
|
||||
return result;
|
||||
}
|
||||
public List<Vehicle> FilterUserVehicles(List<Vehicle> results, int userId)
|
||||
{
|
||||
//user is root user.
|
||||
if (userId == -1)
|
||||
{
|
||||
return results;
|
||||
}
|
||||
var accessibleVehicles = _userAccess.GetUserAccessByUserId(userId);
|
||||
if (accessibleVehicles.Any())
|
||||
{
|
||||
var vehicleIds = accessibleVehicles.Select(x => x.Id.VehicleId);
|
||||
return results.Where(x => vehicleIds.Contains(x.Id)).ToList();
|
||||
}
|
||||
else
|
||||
{
|
||||
return new List<Vehicle>();
|
||||
}
|
||||
}
|
||||
public bool UserCanEditVehicle(int userId, int vehicleId)
|
||||
{
|
||||
if (userId == -1)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
var userAccess = _userAccess.GetUserAccessByVehicleAndUserId(userId, vehicleId);
|
||||
if (userAccess != null)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
public bool DeleteAllAccessToVehicle(int vehicleId)
|
||||
{
|
||||
var result = _userAccess.DeleteAllAccessRecordsByVehicleId(vehicleId);
|
||||
return result;
|
||||
}
|
||||
public bool DeleteAllAccessToUser(int userId)
|
||||
{
|
||||
var result = _userAccess.DeleteAllAccessRecordsByUserId(userId);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
449
Logic/VehicleLogic.cs
Normal file
449
Logic/VehicleLogic.cs
Normal file
@@ -0,0 +1,449 @@
|
||||
using MotoVaultPro.Controllers;
|
||||
using MotoVaultPro.External.Interfaces;
|
||||
using MotoVaultPro.Helper;
|
||||
using MotoVaultPro.Models;
|
||||
|
||||
namespace MotoVaultPro.Logic
|
||||
{
|
||||
public interface IVehicleLogic
|
||||
{
|
||||
VehicleRecords GetVehicleRecords(int vehicleId);
|
||||
decimal GetVehicleTotalCost(VehicleRecords vehicleRecords);
|
||||
int GetMaxMileage(int vehicleId);
|
||||
int GetMaxMileage(VehicleRecords vehicleRecords);
|
||||
int GetMinMileage(int vehicleId);
|
||||
int GetMinMileage(VehicleRecords vehicleRecords);
|
||||
int GetOwnershipDays(string purchaseDate, string soldDate, int year, List<ServiceRecord> serviceRecords, List<CollisionRecord> repairRecords, List<GasRecord> gasRecords, List<UpgradeRecord> upgradeRecords, List<OdometerRecord> odometerRecords, List<TaxRecord> taxRecords);
|
||||
bool GetVehicleHasUrgentOrPastDueReminders(int vehicleId, int currentMileage);
|
||||
List<VehicleInfo> GetVehicleInfo(List<Vehicle> vehicles);
|
||||
List<ReminderRecordViewModel> GetReminders(List<Vehicle> vehicles, bool isCalendar);
|
||||
List<PlanRecord> GetPlans(List<Vehicle> vehicles, bool excludeDone);
|
||||
bool UpdateRecurringTaxes(int vehicleId);
|
||||
void RestoreSupplyRecordsByUsage(List<SupplyUsageHistory> supplyUsage, string usageDescription);
|
||||
}
|
||||
public class VehicleLogic: IVehicleLogic
|
||||
{
|
||||
private readonly IServiceRecordDataAccess _serviceRecordDataAccess;
|
||||
private readonly IGasRecordDataAccess _gasRecordDataAccess;
|
||||
private readonly ICollisionRecordDataAccess _collisionRecordDataAccess;
|
||||
private readonly IUpgradeRecordDataAccess _upgradeRecordDataAccess;
|
||||
private readonly ITaxRecordDataAccess _taxRecordDataAccess;
|
||||
private readonly IOdometerRecordDataAccess _odometerRecordDataAccess;
|
||||
private readonly IReminderRecordDataAccess _reminderRecordDataAccess;
|
||||
private readonly IPlanRecordDataAccess _planRecordDataAccess;
|
||||
private readonly IReminderHelper _reminderHelper;
|
||||
private readonly IVehicleDataAccess _dataAccess;
|
||||
private readonly ISupplyRecordDataAccess _supplyRecordDataAccess;
|
||||
private readonly ILogger<VehicleLogic> _logger;
|
||||
|
||||
public VehicleLogic(
|
||||
IServiceRecordDataAccess serviceRecordDataAccess,
|
||||
IGasRecordDataAccess gasRecordDataAccess,
|
||||
ICollisionRecordDataAccess collisionRecordDataAccess,
|
||||
IUpgradeRecordDataAccess upgradeRecordDataAccess,
|
||||
ITaxRecordDataAccess taxRecordDataAccess,
|
||||
IOdometerRecordDataAccess odometerRecordDataAccess,
|
||||
IReminderRecordDataAccess reminderRecordDataAccess,
|
||||
IPlanRecordDataAccess planRecordDataAccess,
|
||||
IReminderHelper reminderHelper,
|
||||
IVehicleDataAccess dataAccess,
|
||||
ISupplyRecordDataAccess supplyRecordDataAccess,
|
||||
ILogger<VehicleLogic> logger
|
||||
) {
|
||||
_serviceRecordDataAccess = serviceRecordDataAccess;
|
||||
_gasRecordDataAccess = gasRecordDataAccess;
|
||||
_collisionRecordDataAccess = collisionRecordDataAccess;
|
||||
_upgradeRecordDataAccess = upgradeRecordDataAccess;
|
||||
_taxRecordDataAccess = taxRecordDataAccess;
|
||||
_odometerRecordDataAccess = odometerRecordDataAccess;
|
||||
_planRecordDataAccess = planRecordDataAccess;
|
||||
_reminderRecordDataAccess = reminderRecordDataAccess;
|
||||
_reminderHelper = reminderHelper;
|
||||
_dataAccess = dataAccess;
|
||||
_supplyRecordDataAccess = supplyRecordDataAccess;
|
||||
_logger = logger;
|
||||
}
|
||||
public VehicleRecords GetVehicleRecords(int vehicleId)
|
||||
{
|
||||
return new VehicleRecords
|
||||
{
|
||||
ServiceRecords = _serviceRecordDataAccess.GetServiceRecordsByVehicleId(vehicleId),
|
||||
GasRecords = _gasRecordDataAccess.GetGasRecordsByVehicleId(vehicleId),
|
||||
CollisionRecords = _collisionRecordDataAccess.GetCollisionRecordsByVehicleId(vehicleId),
|
||||
TaxRecords = _taxRecordDataAccess.GetTaxRecordsByVehicleId(vehicleId),
|
||||
UpgradeRecords = _upgradeRecordDataAccess.GetUpgradeRecordsByVehicleId(vehicleId),
|
||||
OdometerRecords = _odometerRecordDataAccess.GetOdometerRecordsByVehicleId(vehicleId),
|
||||
};
|
||||
}
|
||||
public decimal GetVehicleTotalCost(VehicleRecords vehicleRecords)
|
||||
{
|
||||
var serviceRecordSum = vehicleRecords.ServiceRecords.Sum(x => x.Cost);
|
||||
var repairRecordSum = vehicleRecords.CollisionRecords.Sum(x => x.Cost);
|
||||
var upgradeRecordSum = vehicleRecords.UpgradeRecords.Sum(x => x.Cost);
|
||||
var taxRecordSum = vehicleRecords.TaxRecords.Sum(x => x.Cost);
|
||||
var gasRecordSum = vehicleRecords.GasRecords.Sum(x => x.Cost);
|
||||
return serviceRecordSum + repairRecordSum + upgradeRecordSum + taxRecordSum + gasRecordSum;
|
||||
}
|
||||
public int GetMaxMileage(int vehicleId)
|
||||
{
|
||||
var numbersArray = new List<int>();
|
||||
var serviceRecords = _serviceRecordDataAccess.GetServiceRecordsByVehicleId(vehicleId);
|
||||
if (serviceRecords.Any())
|
||||
{
|
||||
numbersArray.Add(serviceRecords.Max(x => x.Mileage));
|
||||
}
|
||||
var repairRecords = _collisionRecordDataAccess.GetCollisionRecordsByVehicleId(vehicleId);
|
||||
if (repairRecords.Any())
|
||||
{
|
||||
numbersArray.Add(repairRecords.Max(x => x.Mileage));
|
||||
}
|
||||
var gasRecords = _gasRecordDataAccess.GetGasRecordsByVehicleId(vehicleId);
|
||||
if (gasRecords.Any())
|
||||
{
|
||||
numbersArray.Add(gasRecords.Max(x => x.Mileage));
|
||||
}
|
||||
var upgradeRecords = _upgradeRecordDataAccess.GetUpgradeRecordsByVehicleId(vehicleId);
|
||||
if (upgradeRecords.Any())
|
||||
{
|
||||
numbersArray.Add(upgradeRecords.Max(x => x.Mileage));
|
||||
}
|
||||
var odometerRecords = _odometerRecordDataAccess.GetOdometerRecordsByVehicleId(vehicleId);
|
||||
if (odometerRecords.Any())
|
||||
{
|
||||
numbersArray.Add(odometerRecords.Max(x => x.Mileage));
|
||||
}
|
||||
return numbersArray.Any() ? numbersArray.Max() : 0;
|
||||
}
|
||||
public int GetMaxMileage(VehicleRecords vehicleRecords)
|
||||
{
|
||||
var numbersArray = new List<int>();
|
||||
if (vehicleRecords.ServiceRecords.Any())
|
||||
{
|
||||
numbersArray.Add(vehicleRecords.ServiceRecords.Max(x => x.Mileage));
|
||||
}
|
||||
if (vehicleRecords.CollisionRecords.Any())
|
||||
{
|
||||
numbersArray.Add(vehicleRecords.CollisionRecords.Max(x => x.Mileage));
|
||||
}
|
||||
if (vehicleRecords.GasRecords.Any())
|
||||
{
|
||||
numbersArray.Add(vehicleRecords.GasRecords.Max(x => x.Mileage));
|
||||
}
|
||||
if (vehicleRecords.UpgradeRecords.Any())
|
||||
{
|
||||
numbersArray.Add(vehicleRecords.UpgradeRecords.Max(x => x.Mileage));
|
||||
}
|
||||
if (vehicleRecords.OdometerRecords.Any())
|
||||
{
|
||||
numbersArray.Add(vehicleRecords.OdometerRecords.Max(x => x.Mileage));
|
||||
}
|
||||
return numbersArray.Any() ? numbersArray.Max() : 0;
|
||||
}
|
||||
public int GetMinMileage(int vehicleId)
|
||||
{
|
||||
var numbersArray = new List<int>();
|
||||
var serviceRecords = _serviceRecordDataAccess.GetServiceRecordsByVehicleId(vehicleId).Where(x => x.Mileage != default);
|
||||
if (serviceRecords.Any())
|
||||
{
|
||||
numbersArray.Add(serviceRecords.Min(x => x.Mileage));
|
||||
}
|
||||
var repairRecords = _collisionRecordDataAccess.GetCollisionRecordsByVehicleId(vehicleId).Where(x => x.Mileage != default);
|
||||
if (repairRecords.Any())
|
||||
{
|
||||
numbersArray.Add(repairRecords.Min(x => x.Mileage));
|
||||
}
|
||||
var gasRecords = _gasRecordDataAccess.GetGasRecordsByVehicleId(vehicleId).Where(x => x.Mileage != default);
|
||||
if (gasRecords.Any())
|
||||
{
|
||||
numbersArray.Add(gasRecords.Min(x => x.Mileage));
|
||||
}
|
||||
var upgradeRecords = _upgradeRecordDataAccess.GetUpgradeRecordsByVehicleId(vehicleId).Where(x => x.Mileage != default);
|
||||
if (upgradeRecords.Any())
|
||||
{
|
||||
numbersArray.Add(upgradeRecords.Min(x => x.Mileage));
|
||||
}
|
||||
var odometerRecords = _odometerRecordDataAccess.GetOdometerRecordsByVehicleId(vehicleId).Where(x => x.Mileage != default);
|
||||
if (odometerRecords.Any())
|
||||
{
|
||||
numbersArray.Add(odometerRecords.Min(x => x.Mileage));
|
||||
}
|
||||
return numbersArray.Any() ? numbersArray.Min() : 0;
|
||||
}
|
||||
public int GetMinMileage(VehicleRecords vehicleRecords)
|
||||
{
|
||||
var numbersArray = new List<int>();
|
||||
var _serviceRecords = vehicleRecords.ServiceRecords.Where(x => x.Mileage != default).ToList();
|
||||
if (_serviceRecords.Any())
|
||||
{
|
||||
numbersArray.Add(_serviceRecords.Min(x => x.Mileage));
|
||||
}
|
||||
var _repairRecords = vehicleRecords.CollisionRecords.Where(x => x.Mileage != default).ToList();
|
||||
if (_repairRecords.Any())
|
||||
{
|
||||
numbersArray.Add(_repairRecords.Min(x => x.Mileage));
|
||||
}
|
||||
var _gasRecords = vehicleRecords.GasRecords.Where(x => x.Mileage != default).ToList();
|
||||
if (_gasRecords.Any())
|
||||
{
|
||||
numbersArray.Add(_gasRecords.Min(x => x.Mileage));
|
||||
}
|
||||
var _upgradeRecords = vehicleRecords.UpgradeRecords.Where(x => x.Mileage != default).ToList();
|
||||
if (_upgradeRecords.Any())
|
||||
{
|
||||
numbersArray.Add(_upgradeRecords.Min(x => x.Mileage));
|
||||
}
|
||||
var _odometerRecords = vehicleRecords.OdometerRecords.Where(x => x.Mileage != default).ToList();
|
||||
if (_odometerRecords.Any())
|
||||
{
|
||||
numbersArray.Add(_odometerRecords.Min(x => x.Mileage));
|
||||
}
|
||||
return numbersArray.Any() ? numbersArray.Min() : 0;
|
||||
}
|
||||
public int GetOwnershipDays(string purchaseDate, string soldDate, int year, List<ServiceRecord> serviceRecords, List<CollisionRecord> repairRecords, List<GasRecord> gasRecords, List<UpgradeRecord> upgradeRecords, List<OdometerRecord> odometerRecords, List<TaxRecord> taxRecords)
|
||||
{
|
||||
var startDate = DateTime.Now;
|
||||
var endDate = DateTime.Now;
|
||||
bool usePurchaseDate = false;
|
||||
bool useSoldDate = false;
|
||||
if (!string.IsNullOrWhiteSpace(soldDate) && DateTime.TryParse(soldDate, out DateTime vehicleSoldDate))
|
||||
{
|
||||
if (year == default || year >= vehicleSoldDate.Year) //All Time is selected or the selected year is greater or equal to the year the vehicle is sold
|
||||
{
|
||||
endDate = vehicleSoldDate; //cap end date to vehicle sold date.
|
||||
useSoldDate = true;
|
||||
}
|
||||
}
|
||||
if (!string.IsNullOrWhiteSpace(purchaseDate) && DateTime.TryParse(purchaseDate, out DateTime vehiclePurchaseDate))
|
||||
{
|
||||
if (year == default || year <= vehiclePurchaseDate.Year) //All Time is selected or the selected year is less or equal to the year the vehicle is purchased
|
||||
{
|
||||
startDate = vehiclePurchaseDate; //cap start date to vehicle purchase date
|
||||
usePurchaseDate = true;
|
||||
}
|
||||
}
|
||||
if (year != default)
|
||||
{
|
||||
var calendarYearStart = new DateTime(year, 1, 1);
|
||||
var calendarYearEnd = new DateTime(year + 1, 1, 1);
|
||||
if (!useSoldDate)
|
||||
{
|
||||
endDate = endDate > calendarYearEnd ? calendarYearEnd : endDate;
|
||||
}
|
||||
if (!usePurchaseDate)
|
||||
{
|
||||
startDate = startDate > calendarYearStart ? calendarYearStart : startDate;
|
||||
}
|
||||
var timeElapsed = (int)Math.Floor((endDate - startDate).TotalDays);
|
||||
return timeElapsed;
|
||||
}
|
||||
var dateArray = new List<DateTime>() { startDate };
|
||||
dateArray.AddRange(serviceRecords.Select(x => x.Date));
|
||||
dateArray.AddRange(repairRecords.Select(x => x.Date));
|
||||
dateArray.AddRange(gasRecords.Select(x => x.Date));
|
||||
dateArray.AddRange(upgradeRecords.Select(x => x.Date));
|
||||
dateArray.AddRange(odometerRecords.Select(x => x.Date));
|
||||
dateArray.AddRange(taxRecords.Select(x => x.Date));
|
||||
if (dateArray.Any())
|
||||
{
|
||||
startDate = dateArray.Min();
|
||||
var timeElapsed = (int)Math.Floor((endDate - startDate).TotalDays);
|
||||
return timeElapsed;
|
||||
} else
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
public bool GetVehicleHasUrgentOrPastDueReminders(int vehicleId, int currentMileage)
|
||||
{
|
||||
var reminders = _reminderRecordDataAccess.GetReminderRecordsByVehicleId(vehicleId);
|
||||
var results = _reminderHelper.GetReminderRecordViewModels(reminders, currentMileage, DateTime.Now);
|
||||
return results.Any(x => x.Urgency == ReminderUrgency.VeryUrgent || x.Urgency == ReminderUrgency.PastDue);
|
||||
}
|
||||
|
||||
public List<VehicleInfo> GetVehicleInfo(List<Vehicle> vehicles)
|
||||
{
|
||||
List<VehicleInfo> apiResult = new List<VehicleInfo>();
|
||||
|
||||
foreach (Vehicle vehicle in vehicles)
|
||||
{
|
||||
var currentMileage = GetMaxMileage(vehicle.Id);
|
||||
var reminders = _reminderRecordDataAccess.GetReminderRecordsByVehicleId(vehicle.Id);
|
||||
var results = _reminderHelper.GetReminderRecordViewModels(reminders, currentMileage, DateTime.Now);
|
||||
|
||||
var serviceRecords = _serviceRecordDataAccess.GetServiceRecordsByVehicleId(vehicle.Id);
|
||||
var repairRecords = _collisionRecordDataAccess.GetCollisionRecordsByVehicleId(vehicle.Id);
|
||||
var upgradeRecords = _upgradeRecordDataAccess.GetUpgradeRecordsByVehicleId(vehicle.Id);
|
||||
var gasRecords = _gasRecordDataAccess.GetGasRecordsByVehicleId(vehicle.Id);
|
||||
var taxRecords = _taxRecordDataAccess.GetTaxRecordsByVehicleId(vehicle.Id);
|
||||
var planRecords = _planRecordDataAccess.GetPlanRecordsByVehicleId(vehicle.Id);
|
||||
|
||||
var resultToAdd = new VehicleInfo()
|
||||
{
|
||||
VehicleData = vehicle,
|
||||
LastReportedOdometer = currentMileage,
|
||||
ServiceRecordCount = serviceRecords.Count(),
|
||||
ServiceRecordCost = serviceRecords.Sum(x => x.Cost),
|
||||
RepairRecordCount = repairRecords.Count(),
|
||||
RepairRecordCost = repairRecords.Sum(x => x.Cost),
|
||||
UpgradeRecordCount = upgradeRecords.Count(),
|
||||
UpgradeRecordCost = upgradeRecords.Sum(x => x.Cost),
|
||||
GasRecordCount = gasRecords.Count(),
|
||||
GasRecordCost = gasRecords.Sum(x => x.Cost),
|
||||
TaxRecordCount = taxRecords.Count(),
|
||||
TaxRecordCost = taxRecords.Sum(x => x.Cost),
|
||||
VeryUrgentReminderCount = results.Count(x => x.Urgency == ReminderUrgency.VeryUrgent),
|
||||
PastDueReminderCount = results.Count(x => x.Urgency == ReminderUrgency.PastDue),
|
||||
UrgentReminderCount = results.Count(x => x.Urgency == ReminderUrgency.Urgent),
|
||||
NotUrgentReminderCount = results.Count(x => x.Urgency == ReminderUrgency.NotUrgent),
|
||||
PlanRecordBackLogCount = planRecords.Count(x => x.Progress == PlanProgress.Backlog),
|
||||
PlanRecordInProgressCount = planRecords.Count(x => x.Progress == PlanProgress.InProgress),
|
||||
PlanRecordTestingCount = planRecords.Count(x => x.Progress == PlanProgress.Testing),
|
||||
PlanRecordDoneCount = planRecords.Count(x => x.Progress == PlanProgress.Done)
|
||||
};
|
||||
//set next reminder
|
||||
if (results.Any(x => (x.Metric == ReminderMetric.Date || x.Metric == ReminderMetric.Both) && x.Date >= DateTime.Now.Date))
|
||||
{
|
||||
resultToAdd.NextReminder = results.Where(x => x.Date >= DateTime.Now.Date).OrderBy(x => x.Date).Select(x => new ReminderAPIExportModel { Id = x.Id.ToString(), Description = x.Description, Urgency = x.Urgency.ToString(), Metric = x.Metric.ToString(), UserMetric = x.UserMetric.ToString(), Notes = x.Notes, DueDate = x.Date.ToShortDateString(), DueOdometer = x.Mileage.ToString(), DueDays = x.DueDays.ToString(), DueDistance = x.DueMileage.ToString(), Tags = string.Join(' ', x.Tags) }).First();
|
||||
}
|
||||
else if (results.Any(x => (x.Metric == ReminderMetric.Odometer || x.Metric == ReminderMetric.Both) && x.Mileage >= currentMileage))
|
||||
{
|
||||
resultToAdd.NextReminder = results.Where(x => x.Mileage >= currentMileage).OrderBy(x => x.Mileage).Select(x => new ReminderAPIExportModel { Id = x.Id.ToString(), Description = x.Description, Urgency = x.Urgency.ToString(), Metric = x.Metric.ToString(), UserMetric = x.UserMetric.ToString(), Notes = x.Notes, DueDate = x.Date.ToShortDateString(), DueOdometer = x.Mileage.ToString(), DueDays = x.DueDays.ToString(), DueDistance = x.DueMileage.ToString(), Tags = string.Join(' ', x.Tags) }).First();
|
||||
}
|
||||
apiResult.Add(resultToAdd);
|
||||
}
|
||||
return apiResult;
|
||||
}
|
||||
public List<ReminderRecordViewModel> GetReminders(List<Vehicle> vehicles, bool isCalendar)
|
||||
{
|
||||
List<ReminderRecordViewModel> reminders = new List<ReminderRecordViewModel>();
|
||||
foreach (Vehicle vehicle in vehicles)
|
||||
{
|
||||
var vehicleReminders = _reminderRecordDataAccess.GetReminderRecordsByVehicleId(vehicle.Id);
|
||||
if (isCalendar)
|
||||
{
|
||||
vehicleReminders.RemoveAll(x => x.Metric == ReminderMetric.Odometer);
|
||||
//we don't care about mileages so we can basically fake the current vehicle mileage.
|
||||
}
|
||||
if (vehicleReminders.Any())
|
||||
{
|
||||
var vehicleMileage = isCalendar ? 0 : GetMaxMileage(vehicle.Id);
|
||||
var reminderUrgency = _reminderHelper.GetReminderRecordViewModels(vehicleReminders, vehicleMileage, DateTime.Now);
|
||||
reminderUrgency = reminderUrgency.Select(x => new ReminderRecordViewModel { Id = x.Id, Metric = x.Metric, Date = x.Date, Notes = x.Notes, Mileage = x.Mileage, Urgency = x.Urgency, Description = $"{vehicle.Year} {vehicle.Make} {vehicle.Model} #{StaticHelper.GetVehicleIdentifier(vehicle)} - {x.Description}" }).ToList();
|
||||
reminders.AddRange(reminderUrgency);
|
||||
}
|
||||
}
|
||||
return reminders.OrderByDescending(x=>x.Urgency).ToList();
|
||||
}
|
||||
public List<PlanRecord> GetPlans(List<Vehicle> vehicles, bool excludeDone)
|
||||
{
|
||||
List<PlanRecord> plans = new List<PlanRecord>();
|
||||
foreach (Vehicle vehicle in vehicles)
|
||||
{
|
||||
var vehiclePlans = _planRecordDataAccess.GetPlanRecordsByVehicleId(vehicle.Id);
|
||||
if (excludeDone)
|
||||
{
|
||||
vehiclePlans.RemoveAll(x => x.Progress == PlanProgress.Done);
|
||||
}
|
||||
if (vehiclePlans.Any())
|
||||
{
|
||||
var convertedPlans = vehiclePlans.Select(x => new PlanRecord { ImportMode = x.ImportMode, Priority = x.Priority, Progress = x.Progress, Notes = x.Notes, RequisitionHistory = x.RequisitionHistory, Description = $"{vehicle.Year} {vehicle.Make} {vehicle.Model} #{StaticHelper.GetVehicleIdentifier(vehicle)} - {x.Description}" });
|
||||
plans.AddRange(convertedPlans);
|
||||
}
|
||||
}
|
||||
return plans.OrderBy(x => x.Priority).ThenBy(x=>x.Progress).ToList();
|
||||
}
|
||||
public bool UpdateRecurringTaxes(int vehicleId)
|
||||
{
|
||||
var vehicleData = _dataAccess.GetVehicleById(vehicleId);
|
||||
if (!string.IsNullOrWhiteSpace(vehicleData.SoldDate))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
bool RecurringTaxIsOutdated(TaxRecord taxRecord)
|
||||
{
|
||||
var monthInterval = taxRecord.RecurringInterval != ReminderMonthInterval.Other ? (int)taxRecord.RecurringInterval : taxRecord.CustomMonthInterval;
|
||||
bool addDays = taxRecord.RecurringInterval == ReminderMonthInterval.Other && taxRecord.CustomMonthIntervalUnit == ReminderIntervalUnit.Days;
|
||||
return addDays ? DateTime.Now > taxRecord.Date.AddDays(monthInterval) : DateTime.Now > taxRecord.Date.AddMonths(monthInterval);
|
||||
}
|
||||
var result = _taxRecordDataAccess.GetTaxRecordsByVehicleId(vehicleId);
|
||||
var outdatedRecurringFees = result.Where(x => x.IsRecurring && RecurringTaxIsOutdated(x));
|
||||
if (outdatedRecurringFees.Any())
|
||||
{
|
||||
var success = false;
|
||||
foreach (TaxRecord recurringFee in outdatedRecurringFees)
|
||||
{
|
||||
var monthInterval = recurringFee.RecurringInterval != ReminderMonthInterval.Other ? (int)recurringFee.RecurringInterval : recurringFee.CustomMonthInterval;
|
||||
bool isOutdated = true;
|
||||
bool addDays = recurringFee.RecurringInterval == ReminderMonthInterval.Other && recurringFee.CustomMonthIntervalUnit == ReminderIntervalUnit.Days;
|
||||
//update the original outdated tax record
|
||||
recurringFee.IsRecurring = false;
|
||||
_taxRecordDataAccess.SaveTaxRecordToVehicle(recurringFee);
|
||||
//month multiplier for severely outdated monthly tax records.
|
||||
int monthMultiplier = 1;
|
||||
var originalDate = recurringFee.Date;
|
||||
while (isOutdated)
|
||||
{
|
||||
try
|
||||
{
|
||||
var nextDate = addDays ? originalDate.AddDays(monthInterval * monthMultiplier) : originalDate.AddMonths(monthInterval * monthMultiplier);
|
||||
monthMultiplier++;
|
||||
var nextnextDate = addDays ? originalDate.AddDays(monthInterval * monthMultiplier) : originalDate.AddMonths(monthInterval * monthMultiplier);
|
||||
recurringFee.Date = nextDate;
|
||||
recurringFee.Id = default; //new record
|
||||
recurringFee.IsRecurring = DateTime.Now <= nextnextDate;
|
||||
_taxRecordDataAccess.SaveTaxRecordToVehicle(recurringFee);
|
||||
isOutdated = !recurringFee.IsRecurring;
|
||||
success = true;
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
isOutdated = false; //break out of loop if something broke.
|
||||
success = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
return success;
|
||||
}
|
||||
return false; //no outdated recurring tax records.
|
||||
}
|
||||
public void RestoreSupplyRecordsByUsage(List<SupplyUsageHistory> supplyUsage, string usageDescription)
|
||||
{
|
||||
foreach (SupplyUsageHistory supply in supplyUsage)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (supply.Id == default)
|
||||
{
|
||||
continue; //no id, skip current supply.
|
||||
}
|
||||
var result = _supplyRecordDataAccess.GetSupplyRecordById(supply.Id);
|
||||
if (result != null && result.Id != default)
|
||||
{
|
||||
//supply exists, re-add the quantity and cost
|
||||
result.Quantity += supply.Quantity;
|
||||
result.Cost += supply.Cost;
|
||||
var requisitionRecord = new SupplyUsageHistory
|
||||
{
|
||||
Id = supply.Id,
|
||||
Date = DateTime.Now.Date,
|
||||
Description = $"Restored from {usageDescription}",
|
||||
Quantity = supply.Quantity,
|
||||
Cost = supply.Cost
|
||||
};
|
||||
result.RequisitionHistory.Add(requisitionRecord);
|
||||
//save
|
||||
_supplyRecordDataAccess.SaveSupplyRecordToVehicle(result);
|
||||
}
|
||||
else
|
||||
{
|
||||
_logger.LogError($"Unable to find supply with id {supply.Id}");
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError($"Error restoring supply with id {supply.Id} : {ex.Message}");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user