using Sarand.BusinessMgr.Net.Standard.Base.App;
using Sarand.BusinessMgr.Net.Standard.Base.Bus;
using Sarand.BusinessMgr.Net.Standard.Base.Bus.Control;
using Sarand.Net.Standard.Base.App;
using Sarand.Net.Standard.Base.Bus;
using Sarand.Net.Standard.Base.Core;
using Sarand.Net.Standard.Base.Dbas;
using Sarand.Net.Standard.Base.Security;
using Sarand.Net.Standard.Base.Utils;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Sarand.BusinessMgr.Net.Standard.Base.Consoles
{
///
/// Utilitiy function relating to a core BusinessMgr database instance.
/// For instance:
/// - Initialisation of new repository.
/// - Bringing static (tax codes etc) and reference data up to date.
///
public abstract class SbmCoreInstanceConsole_Base : SaErrorStateItem
{
//-------------------------------------------------------------------
// Definitions.
public const String col_AffilId = "AffilId";
public const String col_AssocId = "AssocId";
public const String col_Name = "Name";
public const String col_Website = "Website";
public const String col_CompanyNo = "CompanyNo";
public const String col_VatReg = "VatReg";
public const String col_FirstName = "FirstName";
public const String col_LastName = "LastName";
public const String col_Email = "Email";
public const String col_UserId = "UserId";
public const String col_UserCode = "UserCode";
///
/// Organisation and User info for Repository initialisation.
///
public class RepositoryInitialisationData
{
public class UserData
{
public SysId AffiliateId { get; set; }
public String FirstName { get; set; }
public String LastName { get; set; }
public String Email { get; set; }
public SysId SecurityItemId { get; set; }
public String Code { get; set; }
public String Password { get; set; }
public bool PasswordSet { get; set; }
}
public UserData User { get; private set; } = new UserData();
public void ClearUser() { User = new UserData(); }
public class OrganisationData
{
public String Name { get; set; }
public String VatReg { get; set; }
public String CompanyNo { get; set; }
public String Website { get; set; }
public SysId AffiliateId { get; set; }
public SysId AssociationId { get; set; }
}
public OrganisationData Organisation { get; private set; } = new OrganisationData();
public void ClearOrganisation() { Organisation = new OrganisationData(); }
}
//-------------------------------------------------------------------
// Data members.
//-------------------------------------------------------------------
// Construction.
protected SbmCoreInstanceConsole_Base()
{
}
protected override void Dispose(bool disposing)
{
if (disposing)
{
}
base.Dispose(disposing);
}
//-------------------------------------------------------------------
// Properties.
public String SecurityGroupCode_Super { get; } = SaSecurityConsole.GroupCode_Super;
// Core instance.
public abstract ISacsDatabase Database_Core { get; }
public abstract SbmInternals Internals_Core { get; }
public abstract DbUpdateInfo DbUpdateInfo_Core { get; }
//-------------------------------------------------------------------
// Utilities.
//-------------------------------------------------------------------
///
/// Is repository database valid.
/// - Contains core tables (Affiliate, Association, Commodity etc).
///
///
///
public bool IsRepositoryValid()
{
ClearErrorState();
HashSet tablesExpected = new HashSet("Affiliate,Association,Commodity,Transaction,Requirement,SaSecItem,SaSecLink".Split(','));
String sql = $"Select {col_Name} From sys.tables";
try
{
using (DbResultSet resultSet = Database_Core.ExecuteQuery(sql))
{
if (resultSet != null)
{
HashSet tablesFound = new HashSet();
while (resultSet != null && resultSet.HasRows && resultSet.NextRecord())
{
NamedValue.Map values = resultSet.GetRecordValues();
tablesFound.Add(values.GetString(col_Name));
}
HashSet tablesNotFound = new HashSet(tablesExpected.Where(item => !tablesFound.ContainsCI(item)));
if (tablesNotFound.Any())
return SetErrorState($"Core tables not found: {String.Join(", ", tablesNotFound)}.");
}
}
}
catch (Exception exc)
{
String msg = "checking repository validity";
SaLogger.Warn($"{SysUtil.Fn} {ExceptionUtil.GetLogMessage(exc, msg)}");
return SetErrorState($"{exc.GetType().Name} {msg}");
}
return OK;
}
//-------------------------------------------------------------------
///
/// Is repository database initialised.
/// - Has a valid internal association.
/// - Has at least one user.
///
public bool IsRepositoryInitialised()
{
ClearErrorState();
RepositoryInitialisationData initData = new RepositoryInitialisationData();
try
{
// Query first internal association.
List sql = new List()
{
$"Select {col_AssocId}=ass.Id, {col_AffilId}=aff.Id, {col_Name}=aff.Name, {col_Website}=aff.Website, {col_CompanyNo}=aff.CompanyNo, {col_VatReg}=aff.VatReg",
$"From Association ass",
$"Left Join Affiliate aff On aff.Id=ass.AffIdOwner And aff.Id=ass.AffIdTarget And aff.Type<>{(int)Sbm.AffiliateType.Individual}",
$"Where ass.Type={(int)Sbm.AssociationType.Internal};"
};
using (DbResultSet resultSet = Database_Core.ExecuteQuery(String.Join(" ", sql)))
{
while (resultSet != null && resultSet.HasRows && resultSet.NextRecord())
{
NamedValue.Map values = resultSet.GetRecordValues();
initData.ClearOrganisation();
initData.Organisation.Name = values.GetString(col_Name);
initData.Organisation.Website = values.GetString(col_Website);
initData.Organisation.CompanyNo = values.GetString(col_CompanyNo);
initData.Organisation.VatReg = values.GetString(col_VatReg);
initData.Organisation.AffiliateId = new SysId(values.GetLong(col_AffilId));
initData.Organisation.AssociationId = new SysId(values.GetLong(col_AssocId));
if (SysId.IsValid(initData.Organisation.AssociationId) && SysId.IsValid(initData.Organisation.AffiliateId))
break;
}
if (!SysId.IsValid(initData.Organisation.AssociationId))
return SetErrorState("Internal association not found.");
if (!SysId.IsValid(initData.Organisation.AffiliateId))
return SetErrorState("Owner affiliate not found.");
}
// Query Employees for the Organisation.
sql = new List()
{
$"Select {col_AffilId}=aff.Id, {col_FirstName}=aff.FirstName, {col_LastName}=aff.LastName, {col_UserId}=aff.UserId, {col_UserCode}=seci.Code",
$"From Association ass",
$"Inner Join Affiliate aff On aff.Id=ass.AffIdTarget And aff.Type={(int)Sbm.AffiliateType.Individual} And IsNull(aff.UserId,0)>0",
$"Left Join SaSecItem seci On seci.Id=aff.UserId",
$"Where ass.AffIdOwner={SysId.ToLong(initData.Organisation.AffiliateId)} And ass.Type={(int)Sbm.AssociationType.Member};"
};
using (DbResultSet resultSet = Database_Core.ExecuteQuery(String.Join(" ", sql)))
{
while (resultSet != null && resultSet.HasRows && resultSet.NextRecord())
{
NamedValue.Map values = resultSet.GetRecordValues();
initData.ClearUser();
initData.User.AffiliateId = new SysId(values.GetLong(col_AffilId));
initData.User.FirstName = values.GetString(col_FirstName);
initData.User.LastName = values.GetString(col_LastName);
initData.User.Email = values.GetString(col_Email);
initData.User.SecurityItemId = new SysId(values.GetLong(col_UserId));
initData.User.Code = values.GetString(col_UserCode);
if (SysId.IsValid(initData.User.AffiliateId) && SysId.IsValid(initData.User.SecurityItemId))
break;
}
if (!SysId.IsValid(initData.User.AffiliateId))
return SetErrorState("User affiliate not found.");
if (!SysId.IsValid(initData.User.SecurityItemId))
return SetErrorState("User security item not found.");
}
if (Internals_Core.SecurityConsole.RequiresPopulation())
return SetErrorState("Security Database requires population.");
}
catch (Exception exc)
{
String msg = "checking repository initialisation";
SaLogger.Warn($"{SysUtil.Fn} {ExceptionUtil.GetLogMessage(exc, msg)}");
return SetErrorState($"{exc.GetType().Name} {msg}");
}
return OK;
}
//-------------------------------------------------------------------
///
/// Validate repository initialisation data.
///
public bool ValidateRepositoryInitialisionData(RepositoryInitialisationData initData)
{
ClearErrorState();
// Organisation name.
if (StringUtil.IsBlank(initData.Organisation.Name))
return SetErrorState("Organisation name must be supplied.");
if ((ulong)StringUtil.GetLength(initData.Organisation.Name) > SbmDbDfn.GetColTextLen(SbmDbDfn.ColumnIdx.Affiliate_Name))
return SetErrorState("Organisation name exceeds max chars.");
// Organisation VAT reg.
if ((ulong)StringUtil.GetLength(initData.Organisation.VatReg) > SbmDbDfn.GetColTextLen(SbmDbDfn.ColumnIdx.Affiliate_VatReg))
return SetErrorState("Company VAT reg exceeds max chars.");
// Organisation Company Number.
if ((ulong)StringUtil.GetLength(initData.Organisation.CompanyNo) > SbmDbDfn.GetColTextLen(SbmDbDfn.ColumnIdx.Affiliate_CompanyNo))
return SetErrorState("Company number exceeds max chars.");
// Organisation Company Number.
if ((ulong)StringUtil.GetLength(initData.Organisation.Website) > SbmDbDfn.GetColTextLen(SbmDbDfn.ColumnIdx.Affiliate_Website))
return SetErrorState("Company website address exceeds max chars.");
// User first name.
if (StringUtil.IsBlank(initData.User.FirstName))
return SetErrorState("User first name must be supplied.");
if ((ulong)StringUtil.GetLength(initData.User.FirstName) > SbmDbDfn.GetColTextLen(SbmDbDfn.ColumnIdx.Affiliate_FirstName))
return SetErrorState("User first name exceeds max chars.");
// User last name.
if (StringUtil.IsBlank(initData.User.LastName))
return SetErrorState("User last name must be supplied.");
if ((ulong)StringUtil.GetLength(initData.User.LastName) > SbmDbDfn.GetColTextLen(SbmDbDfn.ColumnIdx.Affiliate_LastName))
return SetErrorState("User last name exceeds max chars.");
// User email.
if (StringUtil.IsBlank(initData.User.Email))
return SetErrorState("User email must be supplied.");
if ((ulong)StringUtil.GetLength(initData.User.Email) > SbmDbDfn.GetColTextLen(SbmDbDfn.ColumnIdx.Affiliate_Email) || (ulong)StringUtil.GetLength(initData.User.Email) > SaSecurityDbDfn.GetColTextLen(SaSecurityDbDfn.ColumnIdx.SaSecItem_EmailAddress))
return SetErrorState("User email exceeds max chars.");
// User code.
if (StringUtil.IsBlank(initData.User.Code))
return SetErrorState("User code must be supplied.");
if ((ulong)StringUtil.GetLength(initData.User.Code) > SaSecurityDbDfn.GetColTextLen(SaSecurityDbDfn.ColumnIdx.SaSecItem_Code))
return SetErrorState("User code exceeds max chars.");
// 'Super' security group.
if (StringUtil.IsBlank(SecurityGroupCode_Super))
return SetErrorState($"Failed to identify code for 'Super' security group.");
return OK;
}
//-------------------------------------------------------------------
///
/// Initialise repository.
///
public bool InitialiseRepository(RepositoryInitialisationData initData)
{
ClearErrorState();
if (!ValidateRepositoryInitialisionData(initData)) return false;
using (DisposableCollection disposables = new DisposableCollection())
{
//-----------------------------------------------------------
// Organisation Contact.
// Retrieve existing Organisation.
SbmAffiliate affiliateOrganisation = Internals_Core.AccountMgr.GetAffiliateById(initData.Organisation.AffiliateId);
if (affiliateOrganisation == null && SysId.IsValid(initData.Organisation.AffiliateId))
return SetErrorState("Identified Organisation Contact not found.");
if (affiliateOrganisation != null && !affiliateOrganisation.IsOrganisation())
return SetErrorState("Identified Contact is not an organisation.");
if (affiliateOrganisation == null)
affiliateOrganisation = Internals_Core.AccountMgr.GetAffiliateByTypeName(Sbm.AffiliateType.AnyOrganisation, initData.Organisation.Name);
if (affiliateOrganisation == null)
{
// Create new organisation.
affiliateOrganisation = Internals_Core.AccountMgr.CreateAffiliate(Sbm.AffiliateType.Commercial);
}
disposables.Add(affiliateOrganisation);
// Populate properties.
affiliateOrganisation.Name = initData.Organisation.Name;
if (!StringUtil.IsBlank(initData.Organisation.VatReg))
affiliateOrganisation.VatReg = initData.Organisation.VatReg;
if (!StringUtil.IsBlank(initData.Organisation.CompanyNo))
affiliateOrganisation.CompanyNo = initData.Organisation.CompanyNo;
if (!StringUtil.IsBlank(initData.Organisation.Website))
affiliateOrganisation.Website = initData.Organisation.Website;
// Store Organisation Contact.
if (!SysId.IsValid(affiliateOrganisation.Id))
{
if (!Internals_Core.AccountMgr.InsertAffiliate(affiliateOrganisation, DbUpdateInfo_Core))
return SetErrorState($"Failed to insert Organisation contact - {Internals_Core.AccountMgr.ErrorStateMessage}");
}
else
{
if (!Internals_Core.AccountMgr.UpdateAffiliate(affiliateOrganisation, DbUpdateInfo_Core))
return SetErrorState($"Failed to update Organisation contact - {Internals_Core.AccountMgr.ErrorStateMessage}");
}
//-----------------------------------------------------------
// Organisation Internal association.
// Retrieve existing Internal Association for Organisation.
SbmAssociation associationInternal = Internals_Core.AccountMgr.GetAssociationByOwnerTypeTarget(affiliateOrganisation.Id, Sbm.AssociationType.Internal, affiliateOrganisation.Id);
disposables.Add(associationInternal);
if (associationInternal != null && SysId.IsValid(initData.Organisation.AssociationId) && !associationInternal.Id.Equals(initData.Organisation.AssociationId))
// Failed to validate existing Internal Association against supplied Association Id.
return SetErrorState($"Identified Internal Association (Id. {initData.Organisation.AssociationId}) doesn't match existing association ({associationInternal.Id}).");
if (associationInternal == null && SysId.IsValid(initData.Organisation.AssociationId))
{
// No existing Internal Association found for Organisation - retrieve specified Association.
associationInternal = Internals_Core.AccountMgr.GetAssociationById(initData.Organisation.AssociationId);
if (associationInternal == null)
return SetErrorState($"Failed to retrieve supplied Internal Association. Id: {initData.Organisation.AssociationId}.");
disposables.Add(associationInternal);
if (!associationInternal.AffiliateIdOwner.Equals(affiliateOrganisation.Id))
return SetErrorState($"Supplied Internal Association has different Owner ({associationInternal.AffiliateIdOwner}) to Organisation ({affiliateOrganisation.Id}).");
if (!associationInternal.AffiliateIdTarget.Equals(affiliateOrganisation.Id))
return SetErrorState($"Supplied Internal Association has different Target ({associationInternal.AffiliateIdTarget}) to Organisation ({affiliateOrganisation.Id}).");
}
if (associationInternal == null)
associationInternal = Internals_Core.AccountMgr.CreateAssociation(affiliateOrganisation.Id, Sbm.AssociationType.Internal, affiliateOrganisation.Id);
disposables.Add(associationInternal);
// Store Internal Association.
if (!SysId.IsValid(associationInternal.Id))
{
if (!Internals_Core.AccountMgr.InsertAssociation(associationInternal, DbUpdateInfo_Core))
return SetErrorState($"Failed to insert Internal Association for Organisation - {Internals_Core.AccountMgr.ErrorStateMessage}");
}
else
{
if (!Internals_Core.AccountMgr.UpdateAssociation(associationInternal, DbUpdateInfo_Core))
return SetErrorState($"Failed to update Internal Association for Organisation - {Internals_Core.AccountMgr.ErrorStateMessage}");
}
//-----------------------------------------------------------
// Security.
// Core security console.
SaSecurityConsole securityConsole = Internals_Core.SecurityConsole;
// Initialise security data if required.
if (securityConsole.RequiresPopulation() && !securityConsole.PopulateSecurityRights(DbUpdateInfo_Core))
return SetErrorState(securityConsole.ErrorStateMessage);
// User Security Item.
SaSecurityItem securityItemUser = null;
// Retrieve supplied Security Item.
if (SysId.IsValid(initData.User.SecurityItemId))
{
securityItemUser = securityConsole.GetSecurityItemById(SaSecurity.ItemType.User, initData.User.SecurityItemId);
if (securityItemUser == null)
return SetErrorState($"Failed to retrieve supplied User Security Item. Id: {initData.User.SecurityItemId}.");
disposables.Add(securityItemUser);
if (!securityItemUser.Code.Equals(initData.User.Code))
return SetErrorState($"Supplied User Security Item doesn't match supplied code. Expected: {initData.User.Code}. Found: {securityItemUser.Code}.");
}
if (securityItemUser == null)
{
// Retrieve Security Item by code.
securityItemUser = securityConsole.GetSecurityItemByCode(SaSecurity.ItemType.User, initData.User.Code);
disposables.Add(securityItemUser);
}
if (securityItemUser == null)
{
// Create User Security Item.
if (!securityConsole.CreateUser(initData.User.Code, $"{initData.User.LastName}, {initData.User.FirstName}", DbUpdateInfo_Core))
return SetErrorState($"Failed to create User Security Item - {securityConsole.ErrorStateMessage}.");
securityItemUser = securityConsole.GetSecurityItemByCode(SaSecurity.ItemType.User, initData.User.Code);
if (securityItemUser == null)
return SetErrorState($"Failed to retrieve User Security Item after creation. Code: {initData.User.Code}.");
disposables.Add(securityItemUser);
}
// Populate User Security Item properties.
securityItemUser.EmailAddress = initData.User.Email;
if (initData.User.PasswordSet)
securityConsole.SetPassword(securityItemUser, initData.User.Password);
if (!securityConsole.UpdateSecurityItem(securityItemUser, DbUpdateInfo_Core))
return SetErrorState($"Failed to update User Security Item - {securityConsole.ErrorStateMessage}.");
// Assign to 'Super' Group.
SysId securityGroupIdSuper = securityConsole.GetIdForItemCode(SaSecurity.ItemType.Group, SecurityGroupCode_Super);
if (!SysId.IsValid(securityGroupIdSuper))
return SetErrorState($"Failed to identify 'Super' security group. Code: '{SecurityGroupCode_Super}'.");
if (!securityConsole.ItemsAreLinked(securityItemUser.Id, securityGroupIdSuper) && !securityConsole.AssignUserToGroup(securityItemUser.Code, SecurityGroupCode_Super, DbUpdateInfo_Core))
return SetErrorState($"Failed to assign User Security Item to Super Group - {securityConsole.ErrorStateMessage}.");
//-----------------------------------------------------------
// User Contact.
SbmAffiliate affiliateUser = null;
if (SysId.IsValid(initData.User.AffiliateId))
{
// Retrieve supplied User Contact.
affiliateUser = Internals_Core.AccountMgr.GetAffiliateById(initData.User.AffiliateId);
if (affiliateUser == null)
return SetErrorState($"Failed to retrieve User Contact. Id: {initData.User.AffiliateId}.");
disposables.Add(affiliateUser);
}
if (affiliateUser == null)
{
// Retrieve User Contact by User Code.
affiliateUser = Internals_Core.AccountMgr.GetAffiliateByUserId(securityItemUser.Id);
disposables.Add(affiliateUser);
}
if (affiliateUser == null)
{
// Retrieve User Contact by name.
affiliateUser = Internals_Core.AccountMgr.GetIndividualByName(initData.User.FirstName, initData.User.LastName);
disposables.Add(affiliateUser);
}
if (affiliateUser == null)
{
// Create User Contact.
affiliateUser = Internals_Core.AccountMgr.CreateAffiliate(Sbm.AffiliateType.Individual);
disposables.Add(affiliateUser);
}
// Populate User Contact properties.
affiliateUser.FirstName = initData.User.FirstName;
affiliateUser.LastName = initData.User.LastName;
affiliateUser.Email = initData.User.Email;
affiliateUser.UserId = securityItemUser.Id;
// Store User Contact.
if (!SysId.IsValid(affiliateUser.Id))
{
if (!Internals_Core.AccountMgr.InsertAffiliate(affiliateUser, DbUpdateInfo_Core))
return SetErrorState($"Failed to insert User contact - {Internals_Core.AccountMgr.ErrorStateMessage}");
}
else
{
if (!Internals_Core.AccountMgr.UpdateAffiliate(affiliateUser, DbUpdateInfo_Core))
return SetErrorState($"Failed to update User contact - {Internals_Core.AccountMgr.ErrorStateMessage}");
}
//-----------------------------------------------------------
// User Member Association.
// Attempt to retrieve existing Member Association.
SbmAssociation associationMember = Internals_Core.AccountMgr.GetAssociationByOwnerTypeTarget(affiliateOrganisation.Id, Sbm.AssociationType.Member, affiliateUser.Id);
if (associationMember == null)
// Create Member Association.
associationMember = Internals_Core.AccountMgr.CreateAssociation(affiliateOrganisation.Id, Sbm.AssociationType.Member, affiliateUser.Id);
disposables.Add(associationMember);
// Store User Association.
if (!SysId.IsValid(associationMember.Id))
{
if (!Internals_Core.AccountMgr.InsertAssociation(associationMember, DbUpdateInfo_Core))
return SetErrorState($"Failed to insert Member Association for User - {Internals_Core.AccountMgr.ErrorStateMessage}");
}
else
{
if (!Internals_Core.AccountMgr.UpdateAssociation(associationMember, DbUpdateInfo_Core))
return SetErrorState($"Failed to update Member Association for User - {Internals_Core.AccountMgr.ErrorStateMessage}");
}
//-----------------------------------------------------------
}
return OK;
}
//-------------------------------------------------------------------
}
}