Portal signin via Control Instance

This commit is contained in:
Andy Howard
2024-10-29 08:40:47 +00:00
parent bf6bb966bc
commit c5ee285e27
467 changed files with 25771 additions and 3347 deletions
@@ -300,7 +300,7 @@
<data name="label.signout" xml:space="preserve">
<value>Sign-out</value>
</data>
<data name="label.Suppliers" xml:space="preserve">
<data name="label.suppliers" xml:space="preserve">
<value>Suppliers</value>
</data>
<data name="label.type" xml:space="preserve">
@@ -417,4 +417,103 @@
<data name="security.msg.warn.sessionExpired" xml:space="preserve">
<value>Your session has expired.</value>
</data>
<data name="caption.asset.category.create" xml:space="preserve">
<value>Create Asset Category</value>
</data>
<data name="caption.asset.create" xml:space="preserve">
<value>Create Asset</value>
</data>
<data name="caption.asset.category.update" xml:space="preserve">
<value>Update Asset Category</value>
</data>
<data name="caption.asset.update" xml:space="preserve">
<value>Update Asset</value>
</data>
<data name="caption.asset.categories" xml:space="preserve">
<value>Asset Categories</value>
</data>
<data name="caption.asset.category" xml:space="preserve">
<value>Asset Category</value>
</data>
<data name="label.assets" xml:space="preserve">
<value>Assets</value>
</data>
<data name="label.asset" xml:space="preserve">
<value>Asset</value>
</data>
<data name="label.status" xml:space="preserve">
<value>Status</value>
</data>
<data name="label.save" xml:space="preserve">
<value>Save</value>
</data>
<data name="label.serial" xml:space="preserve">
<value>Serial</value>
</data>
<data name="label.item.selected" xml:space="preserve">
<value>Selected item</value>
</data>
<data name="label.items.available" xml:space="preserve">
<value>Available items</value>
</data>
<data name="caption.category.select" xml:space="preserve">
<value>Select Category</value>
</data>
<data name="label.caption" xml:space="preserve">
<value>Caption</value>
</data>
<data name="caption.vehicle.classes" xml:space="preserve">
<value>Vehicle Classes</value>
</data>
<data name="caption.vehicle.class" xml:space="preserve">
<value>Vehicle Class</value>
</data>
<data name="caption.vehicle.class.create" xml:space="preserve">
<value>Create Vehicle Class</value>
</data>
<data name="caption.vehicle.class.update" xml:space="preserve">
<value>Update Vehicle Class</value>
</data>
<data name="label.make" xml:space="preserve">
<value>Make</value>
</data>
<data name="label.model" xml:space="preserve">
<value>Model</value>
</data>
<data name="label.derivative" xml:space="preserve">
<value>Derivative</value>
</data>
<data name="label.vehicle" xml:space="preserve">
<value>Vehicle</value>
</data>
<data name="label.vehicles" xml:space="preserve">
<value>Vehicles</value>
</data>
<data name="caption.code.types" xml:space="preserve">
<value>Code Types</value>
</data>
<data name="caption.code.type" xml:space="preserve">
<value>Code Type</value>
</data>
<data name="label.codes" xml:space="preserve">
<value>Codes</value>
</data>
<data name="caption.code.type.create" xml:space="preserve">
<value>Create Code Type</value>
</data>
<data name="caption.code.type.update" xml:space="preserve">
<value>Update Code Type</value>
</data>
<data name="caption.code.create" xml:space="preserve">
<value>Create Code</value>
</data>
<data name="caption.code.update" xml:space="preserve">
<value>Update Code</value>
</data>
<data name="label.sequence" xml:space="preserve">
<value>Sequence</value>
</data>
<data name="label.code.type" xml:space="preserve">
<value>Code type</value>
</data>
</root>
@@ -7,7 +7,7 @@ using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace Sarand.BusinessMgr.Net.Core.Web.Portal.App
namespace Sarand.BusinessMgr.Web.Portal.App
{
//=======================================================================
public class Program
@@ -1,11 +1,18 @@
using Microsoft.AspNetCore.Hosting;
using DocumentFormat.OpenXml.EMMA;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Hosting;
using Sarand.BusinessMgr.Net.Core.Web.App;
using Sarand.BusinessMgr.Net.Core.Web.Control.Consoles;
using Sarand.BusinessMgr.Net.Core.Web.Helpers;
using Sarand.BusinessMgr.Net.Standard.Base.App;
using Sarand.BusinessMgr.Net.Standard.Base.Bus.Control;
using Sarand.BusinessMgr.Net.Standard.Base.Consoles;
using Sarand.BusinessMgr.Net.Standard.Base.Core;
using Sarand.BusinessMgr.Net.Standard.Base.Mgr.Control;
using Sarand.BusinessMgr.Web.Portal.Helpers;
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.IO;
@@ -16,165 +23,270 @@ using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace Sarand.BusinessMgr.Net.Core.Web.Portal.App
namespace Sarand.BusinessMgr.Web.Portal.App
{
//=======================================================================
public class SbmApplication_Portal : SbmApplication_Web
{
//-------------------------------------------------------------------
// Data members.
private PropertySet properties;
private Dictionary<String, SbmAppConfig> appConfigs = new Dictionary<string, SbmAppConfig>();
private Dictionary<String, SbmEmailConfig> emailConfigs = new Dictionary<string, SbmEmailConfig>();
private ISacsDatabase controlDatabase = null;
// Session contexts.
private static ushort sessionContextIdx_Next = 100;
private class SessionContextData
{
public SessionContextData(SbmSessionContext_Web sessionContext)
{
SessionContext = sessionContext;
}
public SbmSessionContext_Web SessionContext { get; }
public List<string> DbIdentifiers { get; } = new List<string>();
public ISacsDatabase BusMgrDatabase { get; set; }
}
private readonly Dictionary<int, SessionContextData> sessionContexts = new Dictionary<int, SessionContextData>();
//=======================================================================
public class SbmApplication_Portal : SbmApplication_Web
{
//-------------------------------------------------------------------
// Data members.
private PropertySet properties;
private Dictionary<String, SbmAppConfig> appConfigs = new Dictionary<string, SbmAppConfig>();
private Dictionary<String, SbmEmailConfig> emailConfigs = new Dictionary<string, SbmEmailConfig>();
private ISacsDatabase controlDatabase = null;
//-------------------------------------------------------------------
// Construction.
public SbmApplication_Portal(IConfiguration configuration) : base()
{
InitialiseTemporaryLoggingFolder();
try
{
properties = new PropertySet();
SbmGlobals.System = (SbmGlobals_Web.System = (System_Web = new SbmSystem_Web(properties)));
Configuration = configuration;
}
catch (Exception exc)
{
// Session contexts.
private static ushort sessionContextIdx_Next = 100;
private class SessionContextData
{
public SessionContextData(SbmSessionContext_Web sessionContext)
{
SessionContext = sessionContext;
}
public SbmSessionContext_Web SessionContext { get; }
public HashSet<string> DbIdentifiers { get; } = new HashSet<string>();
public ISacsDatabase BusMgrDatabase { get; set; }
public virtual SbmInstanceConfig InstanceConfig => null;
// Control database properties.
public bool IsControl { get { return SysId.IsValid(ControlOwnerId) && SysId.IsValid(ControlRepositoryId); } }
public SysId ControlOwnerId { get; protected set; }
public SysId ControlRepositoryId { get; protected set; }
}
private class SessionContextData_Control : SessionContextData
{
public SessionContextData_Control(SbmSessionContext_Web sessionContext, SysId controlOwnerId, SysId controlRepositoryId) : base(sessionContext)
{
this.ControlOwnerId = controlOwnerId;
this.ControlRepositoryId = controlRepositoryId;
}
private SbmInstanceConfig instanceConfig = null;
public override SbmInstanceConfig InstanceConfig
{
get
{
if (instanceConfig == null)
instanceConfig = new SbmInstanceConfig_Web(ControlOwnerId, ControlRepositoryId);
return instanceConfig;
}
}
}
private readonly Dictionary<int, SessionContextData> sessionContexts = new Dictionary<int, SessionContextData>();
//-------------------------------------------------------------------
// Construction.
public SbmApplication_Portal(IConfiguration configuration) : base()
{
InitialiseTemporaryLoggingFolder();
try
{
properties = new PropertySet();
SbmGlobals.System = (SbmGlobals_Web.System = (System_Web = new SbmSystem_Web(properties)));
Configuration = configuration;
}
catch (Exception exc)
{
SaLogger.Error($"{GetType().Name}.CTR {ExceptionUtil.GetErrorStateMessage(exc)}");
throw;
}
}
throw;
}
if (Instance != null)
SaLogger.Error($"{SysUtil.Fn} Application instance already established.");
Instance = this;
}
//-------------------------------------------------------------------
// Properties.
// Application Configuration.
public override SbmAppConfig AppConfig { get { return appConfigs.Values.ToList().getConfigForApplication(); } }
// Session Configuration.
public override String SessionCookieName { get { return Sbm.Config.Portal.Session.Cookie.Name; } }
public override List<ushort> GetSessionContextIdxs()
{
return sessionContexts.Values.Select(item => item.SessionContext.Index).ToList();
}
public override SbmSessionContext GetSessionContext(ushort index)
{
return sessionContexts.GetValue(index, null)?.SessionContext ?? null;
}
public override ISacsDatabase GetControlDatabase()
{
return controlDatabase;
}
public override ISacsDatabase GetTemplateDatabase()
{
return null;
}
public override List<string> GetDbIdentifiers(SbmSessionContext sessionContext)
{
return sessionContexts.GetValue(sessionContext.Index, null)?.DbIdentifiers ?? new List<string>();
}
public override ISacsDatabase GetBusMgrDatabase(SbmSessionContext sessionContext)
{
return sessionContexts.GetValue(sessionContext.Index, null)?.BusMgrDatabase;
}
public override SbmEmailConfig EmailConfig { get { return emailConfigs.Values.ToList().getConfigForSession(AppConfig); } }
//-------------------------------------------------------------------
// Properties.
public static SbmApplication_Portal Instance { get; private set; }
// Application Configuration.
public override SbmAppConfig AppConfig { get { return appConfigs.Values.ToList().getConfigForApplication(); } }
// Session Configuration.
public override String SessionCookieName { get { return Sbm.Config.Portal.Session.Cookie.Name; } }
// Session Contexts - available BusMgr instances.
public override List<ushort> GetSessionContextIdxs()
{
return sessionContexts.Values.Select(item => item.SessionContext.Index).ToList();
}
public override SbmSessionContext GetSessionContext(ushort index)
{
return sessionContexts.GetValue(index, null)?.SessionContext;
}
public SbmSessionContext GetControlSessionContext(String dbIdentifier)
{
return PopulateControlSessionContext(dbIdentifier)?.SessionContext;
}
public override HashSet<string> GetDbIdentifiers(SbmSessionContext sessionContext)
{
return sessionContexts.GetValue(sessionContext.Index, null)?.DbIdentifiers ?? new HashSet<string>();
}
// Databases.
public override ISacsDatabase GetControlDatabase()
{
return controlDatabase;
}
public override ISacsDatabase GetTemplateDatabase()
{
return null;
}
public override ISacsDatabase GetBusMgrDatabase(SbmSessionContext sessionContext)
{
return sessionContexts.GetValue(sessionContext.Index, null)?.BusMgrDatabase;
}
// Email configuration.
public override SbmEmailConfig EmailConfig { get { return emailConfigs.Values.ToList().getConfigForSession(AppConfig); } }
//-------------------------------------------------------------------
// Initialisation.
/// <summary>Initialise environment - from Startup.</summary>
internal void InitWebHostEnvironment(IWebHostEnvironment env)
{
InitEnvironment(env);
}
/// <summary>Initialise environment - from internal (above) or unit tests.</summary>
protected void InitEnvironment(IWebHostEnvironment env)
{
Development = env.IsDevelopment();
environmentName = env.EnvironmentName;
SaLogger.LogFilenamePrefix = "Sbm-Web-Onl";
SaLogger.IncludeHostInFileMessage = false;
SaLogger.IncludeUserInFileMessage = false;
//-------------------------------------------------------------------
// Create Consoles.
public override SbmEmailConsole CreateEmailConsole(ISbmUserContext_Core userContext)
{
return new SbmEmailConsole_Portal(EmailConfig);
}
try
{
// Load server configuration.
var builder = new ConfigurationBuilder()
.SetBasePath(env.ContentRootPath)
.AddJsonFile("appsettings.json", optional: true, reloadOnChange: false)
.AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true, reloadOnChange: false)
.AddJsonFile($"appsettings.machine.{SbmSystem.ComputerName}.json", optional: false, reloadOnChange: false)
.AddEnvironmentVariables();
SbmConfiguration = builder.Build();
//-------------------------------------------------------------------
// Initialisation.
/// <summary>Initialise environment - from Startup.</summary>
internal void InitWebHostEnvironment(IWebHostEnvironment env)
{
InitEnvironment(env);
}
/// <summary>Initialise environment - from internal (above) or unit tests.</summary>
protected void InitEnvironment(IWebHostEnvironment env)
{
Development = env.IsDevelopment();
environmentName = env.EnvironmentName;
SaLogger.LogFilenamePrefix = "Sbm-Web-Onl";
SaLogger.IncludeHostInFileMessage = false;
SaLogger.IncludeUserInFileMessage = false;
// Application configuration.
appConfigs = SbmAppConfigHelper.ExtractAppConfigs(SbmConfiguration.GetSection("AppConfigs"));
emailConfigs = SbmEmailConfigHelper.ExtractEmailConfigs(SbmConfiguration.GetSection("EmailConfigs"));
LoggingRootFolder = AppConfig.Logging.RootFolder;
try
{
// Load server configuration.
var builder = new ConfigurationBuilder()
.SetBasePath(env.ContentRootPath)
.AddJsonFile("appsettings.json", optional: true, reloadOnChange: false)
.AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true, reloadOnChange: false)
.AddJsonFile($"appsettings.machine.{SbmSystem.ComputerName}.json", optional: false, reloadOnChange: false)
.AddEnvironmentVariables();
SbmConfiguration = builder.Build();
// Application instance.
if (AppConfig.Instance.EqualsCI("Dev")) instanceType = Sbm.InstanceType.Dev;
else if (AppConfig.Instance.ContainsCI("Q")) instanceType = Sbm.InstanceType.QC;
else if (AppConfig.Instance.ContainsCI("UAT")) instanceType = Sbm.InstanceType.UAT;
else if (AppConfig.Instance.EqualsCI("PreProd")) instanceType = Sbm.InstanceType.UAT;
// Application configuration.
appConfigs = SbmAppConfigHelper.ExtractAppConfigs(SbmConfiguration.GetSection("AppConfigs"));
emailConfigs = SbmEmailConfigHelper.ExtractEmailConfigs(SbmConfiguration.GetSection("EmailConfigs"));
LoggingRootFolder = AppConfig.Logging.RootFolder;
// Initialise hard-coded logging path.
if (StringUtil.IsEmpty(LoggingRootFolder))
{
if (SbmSystem.ComputerName.EqualsCI("WIN-BPCLM1QUOMT"))
LoggingRootFolder = @"D:\Sbmonite\Logs\" + (Development ? "Dev" : "Test");
}
if (DirectoryUtil.DirectoryExists(LoggingRootFolder))
SaLogger.LogFolder = LoggingRootFolder;
LogApplicationStart($"{SysUtil.Fn}");
// Application instance.
if (AppConfig.Instance.EqualsCI("Dev")) instanceType = Sbm.InstanceType.Dev;
else if (AppConfig.Instance.ContainsCI("Q")) instanceType = Sbm.InstanceType.QC;
else if (AppConfig.Instance.ContainsCI("UAT")) instanceType = Sbm.InstanceType.UAT;
else if (AppConfig.Instance.EqualsCI("PreProd")) instanceType = Sbm.InstanceType.UAT;
// Initialise hard-coded logging path.
if (StringUtil.IsEmpty(LoggingRootFolder))
{
if (SbmSystem.ComputerName.EqualsCI("WIN-BPCLM1QUOMT"))
LoggingRootFolder = @"D:\Sbmonite\Logs\" + (Development ? "Dev" : "Test");
}
if (DirectoryUtil.DirectoryExists(LoggingRootFolder))
SaLogger.LogFolder = LoggingRootFolder;
LogApplicationStart($"{SysUtil.Fn}");
// Control database connection.
SacsDatabaseConsole.OdbcConfig odbcConfig = new SacsDatabaseConsole.OdbcConfig();
odbcConfig.location = AppConfig.ControlDatabase.Location;
odbcConfig.user = AppConfig.ControlDatabase.User;
odbcConfig.pword = AppConfig.ControlDatabase.Password;
controlDatabase = SacsDatabaseConsole.OpenOdbcDatabase(odbcConfig);
if (controlDatabase.HasErrorState())
SaLogger.Warn($"{SysUtil.Fn} Control Database initialisation error: " + controlDatabase.ErrorStateMessage);
odbcConfig.location = AppConfig.ControlDatabase.Location;
odbcConfig.user = AppConfig.ControlDatabase.User;
odbcConfig.pword = AppConfig.ControlDatabase.Password;
controlDatabase = SacsDatabaseConsole.OpenOdbcDatabase(odbcConfig);
if (controlDatabase.HasErrorState())
SaLogger.Warn($"{SysUtil.Fn} Control Database initialisation error: " + controlDatabase.ErrorStateMessage);
// Configure BusMgr database connections.
foreach (SbmAppConfig.DatabaseConfig dbConfig in AppConfig.BusMgrDatabases)
{
SacsDatabaseConsole.OdbcConfig config = new SacsDatabaseConsole.OdbcConfig();
config.location = dbConfig.Location;
config.user = dbConfig.User;
config.pword = dbConfig.Password;
SacsDatabase coreDatabase = SacsDatabaseConsole.OpenOdbcDatabase(config);
if (!coreDatabase.HasErrorState())
{
// Session context.
ushort sessionContextIdx = sessionContextIdx_Next++;
SessionContextData sessionContextData = new SessionContextData(new SbmSessionContext_Web(sessionContextIdx, Sbm.Application.WebPortal));
sessionContextData.BusMgrDatabase = coreDatabase;
sessionContextData.DbIdentifiers.AddRange(dbConfig.Identifiers);
sessionContexts.Add(sessionContextData.SessionContext.Index, sessionContextData);
}
else SaLogger.Warn($"{SysUtil.Fn} Database: " + coreDatabase.DatabaseName + ". Initialisation error: " + coreDatabase.ErrorStateMessage);
}
}
catch (Exception exc)
{
SaLogger.Error($"{SysUtil.Fn} " + ExceptionUtil.GetLogMessage(exc));
}
}
// Configure BusMgr database connections.
foreach (SbmAppConfig.DatabaseConfig dbConfig in AppConfig.BusMgrDatabases)
{
SacsDatabaseConsole.OdbcConfig config = new SacsDatabaseConsole.OdbcConfig();
config.location = dbConfig.Location;
config.user = dbConfig.User;
config.pword = dbConfig.Password;
SacsDatabase coreDatabase = SacsDatabaseConsole.OpenOdbcDatabase(config);
if (!coreDatabase.HasErrorState())
{
// Session context.
ushort sessionContextIdx = sessionContextIdx_Next++;
SessionContextData sessionContextData = new SessionContextData(new SbmSessionContext_Web(sessionContextIdx, Sbm.Application.WebPortal));
sessionContextData.BusMgrDatabase = coreDatabase;
sessionContextData.DbIdentifiers.UnionWith(dbConfig.Identifiers);
sessionContexts.Add(sessionContextData.SessionContext.Index, sessionContextData);
}
else SaLogger.Warn($"{SysUtil.Fn} Database: " + coreDatabase.DatabaseName + ". Initialisation error: " + coreDatabase.ErrorStateMessage);
}
}
catch (Exception exc)
{
SaLogger.Error($"{SysUtil.Fn} " + ExceptionUtil.GetLogMessage(exc));
}
}
//-------------------------------------------------------------------
}
//=======================================================================
//-------------------------------------------------------------------
private SessionContextData PopulateControlSessionContext(String dbIdentifier)
{
if (StringUtil.IsBlank(dbIdentifier)) return null;
// Check whether context already exists in cache.
SessionContextData sessionContextData = sessionContexts.Values.FirstOrDefault(sc => sc.IsControl && sc.DbIdentifiers.ContainsCI(dbIdentifier));
using (DisposableCollection<IDisposable> disposables = new DisposableCollection<IDisposable>())
{
SbmcInstanceMgr controlInstanceMgr = SbmGlobals_Web.System.CreateControlInstanceMgr();
if (controlInstanceMgr == null) return null;
disposables.Add(controlInstanceMgr);
// Check whether context is defined in Control database (and active).
SbmcRepository.Params prmRepository = new SbmcRepository.Params();
prmRepository.ContextsIncluded.Add(dbIdentifier);
SbmcRepository repository = controlInstanceMgr.GetRepository(prmRepository);
disposables.Add(repository);
// Retrieve Repository Owner.
SbmcOwner owner = controlInstanceMgr.GetOwnerById(repository?.OwnerId);
disposables.Add(owner);
// Validate configuration.
if (repository != null && owner != null)
{
if (owner.IsStatusActive && repository.IsStatusActive)
{
// Configure Core database.
SacsDatabaseConsole.OdbcConfig config = new SacsDatabaseConsole.OdbcConfig();
config.location = $"MsSql:{repository.ServerName}\\{repository.InstanceName}.{repository.DatabaseName}";
config.user = repository.UserName;
config.pword = repository.Password;
SacsDatabase coreDatabase = SacsDatabaseConsole.OpenOdbcDatabase(config);
if (!coreDatabase.HasErrorState())
{
// Create Session Context if not not currently populated.
if (sessionContextData == null)
{
// New Session context.
ushort sessionContextIdx = sessionContextIdx_Next++;
sessionContextData = new SessionContextData_Control(new SbmSessionContext_Web(sessionContextIdx, Sbm.Application.WebPortal), owner.Id, repository.Id);
sessionContexts.Add(sessionContextData.SessionContext.Index, sessionContextData);
SaLogger.Info($"{SysUtil.Fn} Control Instance initialised: Context: {repository.Context}. Database: {repository.DatabaseName}.");
}
else SaLogger.Info($"{SysUtil.Fn} Control Instance refreshed: Context: {repository.Context}. Database: {repository.DatabaseName}.");
// Refresh properties.
sessionContextData.DbIdentifiers.Add(repository.Context);
sessionContextData.BusMgrDatabase = coreDatabase;
}
else SaLogger.Warn($"{SysUtil.Fn} Database: {coreDatabase.DatabaseName}. Initialisation error: {coreDatabase.ErrorStateMessage}");
}
else
{
SaLogger.Warn($"{SysUtil.Fn} Attempt to connect to inactive repository. Context: {dbIdentifier}.");
}
}
}
return sessionContextData;
}
//-------------------------------------------------------------------
}
//=======================================================================
}
@@ -5,7 +5,7 @@ using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace Sarand.BusinessMgr.Net.Core.Web
namespace Sarand.BusinessMgr.Web
{
// <summary>
/// Dummy classes to group shared resources - named as per '.resx' file in root namespace.
@@ -19,7 +19,7 @@ namespace Sarand.BusinessMgr.Net.Core.Web
// Ref. Project Properties - Application.
}
namespace Sarand.BusinessMgr.Net.Core.Web.Portal.App
namespace Sarand.BusinessMgr.Web.Portal.App
{
//=======================================================================
public class SbmLocaliser_Portal : SbmLocaliser
@@ -1,73 +1,99 @@
using Newtonsoft.Json;
using Sarand.BusinessMgr.Net.Core.Web.App;
using Sarand.BusinessMgr.Net.Core.Web.Portal.Controllers;
using Sarand.BusinessMgr.Web.Portal.Controllers;
using Sarand.BusinessMgr.Net.Standard.Base.App;
using Sarand.BusinessMgr.Net.Standard.Base.Bus;
using Sarand.Net.Standard.Base.Bus;
using Sarand.Net.Standard.Base.Core;
using Sarand.Net.Standard.Base.Dbas;
using Sarand.Net.Standard.Base.Helpers;
using Sarand.Net.Standard.Base.Security;
using Sarand.Net.Standard.Base.Utils;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Threading.Tasks;
using static Sarand.BusinessMgr.Net.Standard.Base.App.Sbm.Config;
using static Sarand.BusinessMgr.Net.Standard.Base.App.Sbm.Config.Database;
using Sarand.BusinessMgr.Net.Standard.Base.Core;
using Sarand.BusinessMgr.Net.Standard.Base.Mgr;
namespace Sarand.BusinessMgr.Net.Core.Web.Portal.App
namespace Sarand.BusinessMgr.Web.Portal.App
{
//=======================================================================
public class SbmSessionData_Portal : SbmSessionData_Web
public class SbmSessionData_Portal : SbmSessionData_Web, ISbmUserContext_Core
{
//-------------------------------------------------------------------
// Construction.
[JsonConstructor]
private SbmSessionData_Portal(long sessionIdx, int itemIdx, SbmSessionContext sessionContext) :
base(sessionIdx, itemIdx, sessionContext)
{}
{
securityProfile = new SbmSecurityProfile(this, false);
Organisation = new OrganisationData_Portal(null, null);
}
/// <summary>
/// SessionData construction.
/// <para>SessionContext and UserAccount must be supplied.</para>
/// <para>Organisation is optional.</para>
/// </summary>
public SbmSessionData_Portal(SbmSessionContext sessionContext, String dbIdentifier, SaSecurityItem securityItemUser, SbmAffiliate affiliateUser, List<SysIdString> supervisedOrganisations, SbmAssociation associationOrganisation, SbmAffiliate affiliateOrganisation) :
base(sessionContext, securityItemUser, affiliateUser, supervisedOrganisations, associationOrganisation, affiliateOrganisation)
base(sessionContext)
{
DbIdentifier = dbIdentifier;
securityProfile = new SbmSecurityProfile(this, false);
Organisation = new OrganisationData_Portal(associationOrganisation, affiliateOrganisation);
User = new UserData_Portal(sessionContext, securityItemUser, affiliateUser, supervisedOrganisations);
}
//-------------------------------------------------------------------
// Contact details.
private class UserData_Portal : UserData
{
public UserData_Portal(SbmSessionContext sessionContext, SaSecurityItem securityItemUser, SbmAffiliate affiliateUser, List<SysIdString> supervisedOrganisations) :
base(sessionContext, securityItemUser, affiliateUser, supervisedOrganisations)
{ }
public UserData_Portal(UserData userData) :
base(userData)
{ }
public new bool Refresh(SaSecurityItem securityItemUser, SbmAffiliate affiliateUser, List<SysIdString> supervisedOrganisations)
{
return base.Refresh(securityItemUser, affiliateUser, supervisedOrganisations);
}
}
protected override UserData CreateUserData(SbmSessionContext sessionContext, SaSecurityItem securityItemUser, SbmAffiliate affiliateUser, List<SysIdString> supervisedOrganisations)
{
return new UserData_Portal(sessionContext, securityItemUser, affiliateUser, supervisedOrganisations);
}
/// <summary>User - JSON deserialisation route.</summary>
//public new UserData User { get { return base.User; } set { SetUser(new UserData_Portal(value)); } }
// Properties.
/// <summary>
/// Update the user's contact details against the session.
/// <para>- User identity must match that already against the session.</para>
/// </summary>
public bool RefreshUser(SaSecurityItem securityItemUser, SbmAffiliate affiliateUser, List<SysIdString> supervisedOrganisations, SbmAssociation associationOrganisation, SbmAffiliate affiliateOrganisation)
// User properties.
[JsonIgnore]
/// <summary>User Security Item Id.</summary>
public SysId UserSecurityItemId { get { return User?.SecurityItemId; } }
[JsonIgnore]
/// <summary>User Affiliate Id.</summary>
public SysId UserAffiliateId { get { return User?.AffiliateId; } }
[JsonIgnore]
/// <summary>User display name.</summary>
public override String UserDisplayName { get { return User?.DisplayName; } }
[JsonIgnore]
/// <summary>User email address.</summary>
public override String UserEmailAddress { get { return User?.EmailAddress; } }
[JsonIgnore]
public override bool ValidUser { get { return SysId.IsValid(UserSecurityItemId) && SysId.IsValid(UserAffiliateId); } }
[JsonIgnore]
public override bool KeepLoggedIn { get { return User.KeepLoggedIn; } }
// Organisation properties.
[JsonIgnore]
/// <summary>Organisation Affiliate Id.</summary>
public SysId OrganisationAffiliateId { get { return Organisation?.AffiliateId; } }
[JsonIgnore]
/// <summary>Organisation Affiliate Type.</summary>
public Sbm.AffiliateType OrganisationAffiliateType { get { return Organisation?.AffiliateType ?? Sbm.AffiliateType.Unknown; } }
[JsonIgnore]
/// <summary>Organisation 'Internal' Association Id.</summary>
public SysId OrganisationAssociationId { get { return Organisation?.AssociationId; } }
[JsonIgnore]
/// <summary>Organisation display name.</summary>
public String OrganisationDisplayName { get { return Organisation?.DisplayName; } }
[JsonIgnore]
/// <summary>Organisation Interface context - to qualify interfaces belonging to the organisation.</summary>
public String OrganisationInterfaceContext { get { return Organisation?.InterfaceContext; } }
// Database properties.
[JsonIgnore]
public override DbUpdateInfo DbUpdateInfo
{
UserData_Portal user = User as UserData_Portal;
if (user != null)
return user.Refresh(securityItemUser, affiliateUser, supervisedOrganisations);
organisationData = new OrganisationData(associationOrganisation, affiliateOrganisation);
return false;
get
{
return new DbUpdateInfo(User?.AffiliateId, StringUtil.OnEmpty(User.DisplayName, User.EmailAddress), SbmSystem.ComputerName);
}
}
//-------------------------------------------------------------------
@@ -76,6 +102,111 @@ namespace Sarand.BusinessMgr.Net.Core.Web.Portal.App
/// <summary>Database context specified on sign-in.</summary>
public String DbIdentifier { get; set; }
//-------------------------------------------------------------------
// Security Profile.
private readonly SbmSecurityProfile securityProfile;
public ISbmSecurityProfile_Core SecurityProfile_Core { get { return securityProfile; } }
//-------------------------------------------------------------------
// Organisation.
public class OrganisationData_Portal
{
private OrganisationData_Portal() { }
public OrganisationData_Portal(SbmAssociation associationOrganisation, SbmAffiliate affiliateOrganisation)
{
AffiliateId = affiliateOrganisation?.Id ?? null;
AffiliateType = affiliateOrganisation?.AffiliateType ?? Sbm.AffiliateType.Unknown;
AssociationId = associationOrganisation?.Id ?? null;
DisplayName = affiliateOrganisation?.GetDisplayName() ?? "";
InterfaceContext = SbmAccountMgr.GetInterfaceContext(AffiliateId);
}
public SysId AffiliateId { get; protected set; }
public Sbm.AffiliateType AffiliateType { get; protected set; }
public SysId AssociationId { get; protected set; }
public String DisplayName { get; protected set; }
public String InterfaceContext { get; }
}
public OrganisationData_Portal Organisation { get; private set; }
//-------------------------------------------------------------------
// User details.
public class UserData_Portal : UserData
{
[JsonConstructor]
private UserData_Portal() { }
public UserData_Portal(SbmSessionContext sessionContext, SaSecurityItem securityItemUser, SbmAffiliate affiliateUser, List<SysIdString> supervisedOrganisations) :
base(sessionContext)
{
if (securityItemUser != null)
{
SecurityItemId = securityItemUser.Id;
}
if (affiliateUser != null)
{
AffiliateId = affiliateUser.Id;
Refresh(securityItemUser, affiliateUser, supervisedOrganisations);
if (sessionContext.Application == Sbm.Application.WebControl)
KeepLoggedIn = affiliateUser.KeepLoggedIn_Control;
else if (sessionContext.Application == Sbm.Application.WebPortal)
KeepLoggedIn = affiliateUser.KeepLoggedIn_Portal;
else Debugger.Break();
}
this.supervisedOrganisations = supervisedOrganisations ?? new List<SysIdString>();
}
public UserData_Portal(UserData userData) :
base(userData)
{
UserData_Portal userData_Portal = userData as UserData_Portal;
if (userData_Portal != null)
{
SecurityItemId = userData_Portal.SecurityItemId;
AffiliateId = userData_Portal.AffiliateId;
supervisedOrganisations = userData_Portal.supervisedOrganisations;
}
}
// Properties.
public SysId SecurityItemId { get; protected set; }
public SysId AffiliateId { get; protected set; }
public bool KeepLoggedIn { get; private set; }
/// <summary>User managed associations - Id and display name.</summary>
public List<SysIdString> SupervisedOrganisations { get { return new List<SysIdString>(supervisedOrganisations); } }
protected List<SysIdString> supervisedOrganisations { get; } = new List<SysIdString>();
// Utilities.
public bool Refresh(SaSecurityItem securityItemUser, SbmAffiliate affiliateUser, List<SysIdString> supervisedOrganisations)
{
base.Refresh();
// Check that user matches the session.
if (!SysId.IsValid(SecurityItemId) || !SysId.IsValid(securityItemUser?.Id ?? null) || !SecurityItemId.Equals(securityItemUser.Id)) return false;
if (!SysId.IsValid(AffiliateId) || !SysId.IsValid(affiliateUser?.Id ?? null) || !AffiliateId.Equals(affiliateUser.Id)) return false;
// Refresh properties.
EmailAddress = securityItemUser.EmailAddress;
Title = affiliateUser.Title;
FirstName = affiliateUser.FirstName;
LastName = affiliateUser.LastName;
DisplayName = affiliateUser.GetDisplayName();
this.supervisedOrganisations.Clear();
this.supervisedOrganisations.AddRange(supervisedOrganisations);
return true;
}
}
/// <summary>Portal User details.</summary>
public UserData_Portal User { get; private set; }
/// <summary>
/// Update the user's contact details against the session.
/// <para>- User identity must match that already against the session.</para>
/// </summary>
public bool RefreshUser(SaSecurityItem securityItemUser, SbmAffiliate affiliateUser, List<SysIdString> supervisedOrganisations, SbmAssociation associationOrganisation, SbmAffiliate affiliateOrganisation)
{
if (User != null && User.Refresh(securityItemUser, affiliateUser, supervisedOrganisations))
{
Organisation = new OrganisationData_Portal(associationOrganisation, affiliateOrganisation);
return true;
}
return false;
}
//-------------------------------------------------------------------
/// <summary>
/// User Profile.
@@ -137,11 +268,9 @@ namespace Sarand.BusinessMgr.Net.Core.Web.Portal.App
}
public UserProfileData Profile { get; protected set; } = new UserProfileData();
//-------------------------------------------------------------------
//-------------------------------------------------------------------
// Portal application encryption IV value - for consistent encryption during session.
public String Base64IV_PortalApp { get; private set; } = SacsEncryptionHelper.InitSymmetricEncryptionIV(SbmController_Portal.Base64Key_OnlineApp);
public String Base64IV_PortalApp { get; private set; } = SacsEncryptionHelper.InitSymmetricEncryptionIV(SbmController_Portal.Base64Key_PortalApp);
//-------------------------------------------------------------------
}
@@ -11,7 +11,7 @@ using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace Sarand.BusinessMgr.Net.Core.Web.Portal.App
namespace Sarand.BusinessMgr.Web.Portal.App
{
//=======================================================================
public class SbmStartup_Portal : SbmStartup_Web
@@ -0,0 +1,19 @@
using Sarand.BusinessMgr.Net.Core.Web.Consoles;
using Sarand.BusinessMgr.Net.Standard.Base.Consoles;
namespace Sarand.BusinessMgr.Net.Core.Web.Control.Consoles
{
//=======================================================================
public class SbmEmailConsole_Portal : SbmEmailConsole_Web
{
//-------------------------------------------------------------------
// Construction.
public SbmEmailConsole_Portal(SbmEmailConfig emailConfig) :
base(emailConfig)
{
}
//-------------------------------------------------------------------
}
//=======================================================================
}
@@ -0,0 +1,289 @@
using Microsoft.AspNetCore.Mvc;
using Sarand.BusinessMgr.Net.Standard.Base.App;
using Sarand.BusinessMgr.Net.Standard.Base.Bus;
using Sarand.BusinessMgr.Net.Standard.Base.Bus.Control;
using Sarand.BusinessMgr.Web.Portal.Models;
using Sarand.BusinessMgr.Web.Portal.Models.Assets;
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.Utils;
using System;
using System.Collections.Generic;
namespace Sarand.BusinessMgr.Web.Portal.Controllers.Instance
{
//=======================================================================
partial class AssetCategoryController
{
//-------------------------------------------------------------------
// Definitions.
// AssetCategory data.
private class AssetCategoryListData : AjaxResponse_Data.ItemData_Base
{
public String TypeDesc { get; set; }
public String Code { get; set; }
public String DescBrief { get; set; }
public long? AssetCount { get; set; }
}
private class AssetCategoryItemData : AssetCategoryListData
{
public String TypeCode { get; set; }
}
private void PopulateAssetCategoryResponseListData(SbmModel_Portal model, AssetCategoryListData data, SbmAssetCategory assetCategory)
{
data.Id = SysId.ToString(assetCategory.Id);
data.TypeDesc = Internals.AssetTypeCodes.GetDescForType(assetCategory.AssetType);
data.Code = assetCategory.Code;
data.DescBrief = assetCategory.DescBrief;
data.StatusCode = assetCategory.StatusCode;
data.StatusDesc = assetCategory.StatusDesc;
}
private void PopulateAssetCategoryResponseItemData(SbmModel_Portal model, AssetCategoryItemData data, SbmAssetCategory assetCategory)
{
PopulateAssetCategoryResponseListData(model, data, assetCategory);
data.TypeCode = Internals.AssetTypeCodes.GetCodeForType(assetCategory.AssetType);
data.Notes = assetCategory.Notes;
}
//-------------------------------------------------------------------
// Get AssetCategory List ajax request.
//-------------------------------------------------------------------
private class AjaxResponse_AssetCategoryList : AjaxResponse_Data
{
// Construction.
public AjaxResponse_AssetCategoryList(SbmModel_Portal model) : base(model) { }
// AssetCategory list.
public List<AssetCategoryListData> AssetCategories { get; } = new List<AssetCategoryListData>();
// Reference data.
public List<CodeDesc> Statuses { get; } = new List<CodeDesc>();
public List<CodeDesc> StatusesCreate { get; } = new List<CodeDesc>();
public String StatusCodeDefault { get; set; }
}
//-------------------------------------------------------------------
[HttpPost]
[Route("Assets/AssetCategory/Ajax/GetList")]
[Route("Assets/AssetCategory/Ajax/GetAssetCategories")]
[Route("Assets/AssetCategory/Ajax/GetAssetCategoryList")]
public IActionResult Ajax_GetAssetCategoryList(Model_AssetCategory model)
{
String fn = GetType().Name + ".Ajax_GetAssetCategoryList";
String messageTitle = Localiser["caption.asset.categories"];
AjaxResponse_AssetCategoryList responseData = new AjaxResponse_AssetCategoryList(model);
// Validate session and referrer.
if (!ValidReferer) return Forbid();
if (!ValidUserSession) return GetAjaxResult_InvalidSession(responseData);
RefreshModel(model);
// Reference data - statuses.
foreach (SaStatus status in SbmAssetCategory.ValidStatuses)
{
responseData.Statuses.Add(SacsSystem.GetStatusCode(status), Localiser.GetStatusCaption(status));
if (status != SaStatus.Deleted)
responseData.StatusesCreate.Add(SacsSystem.GetStatusCode(status), Localiser.GetStatusCaption(status));
}
responseData.StatusCodeDefault = SacsSystem.GetStatusCode(SbmAssetCategory.DefaultStatus);
// Item filter.
SbmAssetCategory.Params prmAssetCategory = new SbmAssetCategory.Params();
prmAssetCategory.Search = model.Filter.Search;
prmAssetCategory.StatusCat = model.Filter.GetStatusCat();
model.Paging.ItemTotal = (ulong)AssetMgr.GetAssetCategoryCount(prmAssetCategory);
model.PopulatePagingTo(prmAssetCategory.ResultsPaging);
// Item columns.
ColumnDfn colId = AssetMgr.GetAssetCategoryColumnDfn(SbmAssetCategory.Column.Id);
ColumnDfn colType = AssetMgr.GetAssetCategoryColumnDfn(SbmAssetCategory.Column.AssetType_Desc);
ColumnDfn colCode = AssetMgr.GetAssetCategoryColumnDfn(SbmAssetCategory.Column.Code);
ColumnDfn colDescBrief = AssetMgr.GetAssetCategoryColumnDfn(SbmAssetCategory.Column.DescBrief);
ColumnDfn colAssetCount = AssetMgr.GetAssetCategoryColumnDfn(SbmAssetCategory.Column.AssetCount);
ColumnDfn colStatus = AssetMgr.GetAssetCategoryColumnDfn(SbmAssetCategory.Column.Status);
List<ColumnDfn> columns = new List<ColumnDfn>()
{
colId, colType, colCode, colDescBrief,
colAssetCount, colStatus
};
// Query items.
using (DbResultSet resultSet = AssetMgr.ExecuteAssetCategoryQuery(columns, prmAssetCategory))
{
while (resultSet != null && resultSet.HasRows && resultSet.NextRecord())
{
NamedValue.Map values = resultSet.GetRecordValues();
Sbm.AssetType assetType = (Sbm.AssetType)values.GetLong(colType.DataName);
AssetCategoryListData assetCategoryData = new AssetCategoryListData()
{
Id = values.GetLong(colId.DataName).ToString(),
TypeDesc = Localiser.GetAssetTypeCaption(assetType),
Code = values.GetString(colCode.DataName),
DescBrief = values.GetString(colDescBrief.DataName),
StatusCode = values.GetString(colStatus.DataName),
StatusDesc = SacsSystem.GetStatusDescForCode(values.GetString(colStatus.DataName))
};
long assetCount = values.GetLong(colAssetCount.DataName);
if (assetCount > 0)
assetCategoryData.AssetCount = assetCount;
responseData.AssetCategories.Add(assetCategoryData);
}
}
if (model.Paging.IsPaged)
model.Paging.ItemCount = (ushort)responseData.AssetCategories.Count;
// Complete response.
responseData.SetPaging(model);
responseData.Success = true;
return Json(responseData);
}
//-------------------------------------------------------------------
// Get AssetCategory ajax request.
//-------------------------------------------------------------------
private class AjaxResponse_AssetCategory : AjaxResponse_Data
{
// Construction.
public AjaxResponse_AssetCategory(SbmModel_Portal model) : base(model) { }
// AssetCategory details.
public AssetCategoryItemData assetCategory { get; } = new AssetCategoryItemData();
}
//-------------------------------------------------------------------
[HttpPost]
[Route("Assets/AssetCategory/Ajax/GetItem")]
[Route("Assets/AssetCategory/Ajax/GetAssetCategory")]
public IActionResult Ajax_GetAssetCategory(Model_AssetCategory model)
{
String fn = GetType().Name + ".Ajax_GetAssetCategory";
String messageTitle = Localiser["caption.asset.category"];
AjaxResponse_AssetCategory responseData = new AjaxResponse_AssetCategory(model);
// Validate session and referrer.
if (!ValidReferer) return Forbid();
if (!ValidUserSession) return GetAjaxResult_InvalidSession(responseData);
RefreshModel(model);
// Record disposable items as they are created so all can be disposed when needed.
using (DisposableCollection<SacsHierarchyItem> disposables = new DisposableCollection<SacsHierarchyItem>())
{
// Identify item.
SysId assetCategoryId = model.AssetCategory.Id.ToSysId();
if (!SysId.IsValid(assetCategoryId))
return Json(responseData.SetMessage(messageTitle, Localiser["msg.error.item.id.not.specified"]));
// Retrieve item.
SbmAssetCategory assetCategory = AssetMgr.GetAssetCategoryById(assetCategoryId);
disposables.Add(assetCategory);
if (assetCategory == null)
{
SaLogger.Warn($"{SysUtil.Fn} Failed to retrieve assetCategory for Id: '" + assetCategoryId + "'");
return Json(responseData.SetMessage(messageTitle, Localiser["msg.error.item.id.not.found"]));
}
// Construct response.
PopulateAssetCategoryResponseItemData(model, responseData.assetCategory, assetCategory);
responseData.Success = true;
}
return Json(responseData);
}
//-------------------------------------------------------------------
// Store AssetCategory ajax request.
//-------------------------------------------------------------------
[HttpPost]
[Route("Assets/AssetCategory/Ajax/StoreItem")]
[Route("Assets/AssetCategory/Ajax/StoreAssetCategory")]
public IActionResult Ajax_StoreAssetCategory(Model_AssetCategory model)
{
String fn = GetType().Name + ".Ajax_StoreAssetCategory";
String messageTitle = Localiser["caption.asset.category"];
AjaxResponse_AssetCategory responseData = new AjaxResponse_AssetCategory(model);
// Validate session and referrer.
if (!ValidReferer) return Forbid();
if (!ValidUserSession) return GetAjaxResult_InvalidSession(responseData);
RefreshModel(model);
// Validate item Id.
SysId assetCategoryId = model.AssetCategory.Id.ToSysId();
if (StringUtil.ToBool(model.AssetCategory.Exists) && !SysId.IsValid(assetCategoryId))
{
SaLogger.Warn($"{SysUtil.Fn} AssetCategory Id not supplied for existing item.");
return Json(responseData.SetMessage(messageTitle, Localiser["msg.error.system"]));
}
if (!StringUtil.ToBool(model.AssetCategory.Exists) && SysId.IsValid(assetCategoryId))
{
SaLogger.Warn($"{SysUtil.Fn} AssetCategory Id supplied for new item.");
return Json(responseData.SetMessage(messageTitle, Localiser["msg.error.system"]));
}
// Validate item properties.
model.ValidateAssetCategoryData(responseData);
if (responseData.HasControlErrors())
return Json(responseData);
// Record disposable items as they are created so all can be disposed when needed.
using (DisposableCollection<SacsHierarchyItem> disposables = new DisposableCollection<SacsHierarchyItem>())
{
// Retrieve existing item.
SbmAssetCategory assetCategory = AssetMgr.GetAssetCategoryById(assetCategoryId);
disposables.Add(assetCategory);
if (assetCategory == null && SysId.IsValid(assetCategoryId))
{
SaLogger.Warn($"{SysUtil.Fn} Failed to retrieve Asset Category for Id: '" + model.AssetCategory.Id + "'");
return Json(responseData.SetMessage(messageTitle, Localiser["msg.error.item.id.not.found"]));
}
// Create new item if required.
if (assetCategory == null)
{
if (model.AssetCategory.AssetType == 0)
{
// Should be picked up by model validation unless form doesn't include Asset Type.
SaLogger.Warn($"{SysUtil.Fn} Asset Type not specified for new Asset Category.");
return Json(responseData.SetMessage(messageTitle, Localiser["msg.error.system"]));
}
assetCategory = AssetMgr.CreateAssetCategory(model.AssetCategory.AssetType);
}
// Populate item properties.
if (model.AssetCategory.Code != null)
assetCategory.Code = model.AssetCategory.Code;
if (model.AssetCategory.DescBrief != null)
assetCategory.DescBrief = model.AssetCategory.DescBrief;
if (model.AssetCategory.StatusCode != null)
assetCategory.Status = model.AssetCategory.GetStatus();
if (model.AssetCategory.Notes != null)
assetCategory.Notes = model.AssetCategory.Notes;
// Store item.
if (!SysId.IsValid(assetCategory.Id))
{
if (!AssetMgr.InsertAssetCategory(assetCategory, DbUpdateInfo))
{
SaLogger.Warn($"{SysUtil.Fn} Failed to insert Asset Category: " + AssetMgr.ErrorStateMessage);
return Json(responseData.SetMessage(messageTitle, Localiser["msg.error.item.insert.failed"]));
}
}
else
{
if (!AssetMgr.UpdateAssetCategory(assetCategory, DbUpdateInfo))
{
SaLogger.Warn($"{SysUtil.Fn} Failed to update Asset Category [Id:" + SysId.ToString(assetCategory.Id) + "]: " + AssetMgr.ErrorStateMessage);
return Json(responseData.SetMessage(messageTitle, Localiser["msg.error.item.update.failed"]));
}
}
// Construct response.
PopulateAssetCategoryResponseItemData(model, responseData.assetCategory, assetCategory);
responseData.Success = true;
}
return Json(responseData);
}
//-------------------------------------------------------------------
}
//=======================================================================
}
@@ -0,0 +1,49 @@
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Logging;
using Sarand.BusinessMgr.Net.Core.Web.App;
using Sarand.BusinessMgr.Web.Portal.Models;
using Sarand.BusinessMgr.Web.Portal.Models.Assets;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Threading.Tasks;
namespace Sarand.BusinessMgr.Web.Portal.Controllers.Instance
{
//======================================================================
public partial class AssetCategoryController : SbmController_Portal
{
//-------------------------------------------------------------------
// Data members.
//-------------------------------------------------------------------
// Construction.
public AssetCategoryController(SbmLocaliser localiser, ILogger<AssetCategoryController> logger) :
base(localiser, logger)
{
}
//-------------------------------------------------------------------
[Route("Assets/AssetCategory")]
public IActionResult Index()
{
if (!ValidUserSession)
AutoLoginUser();
// Redirect to sign-in if not signed-in.
if (!ValidUserSession)
{
ClearKeepLoggedInCookie();
return RedirectToAction("", "Security");
}
// AssetCategory page.
Model_AssetCategory model = new Model_AssetCategory(Localiser);
return View("Views/Assets/AssetCategory/Index.cshtml", model);
}
//-------------------------------------------------------------------
}
//======================================================================
}
@@ -0,0 +1,327 @@
using Microsoft.AspNetCore.Mvc;
using Sarand.BusinessMgr.Net.Standard.Base.App;
using Sarand.BusinessMgr.Net.Standard.Base.Bus;
using Sarand.BusinessMgr.Net.Standard.Base.Bus.Control;
using Sarand.BusinessMgr.Web.Portal.Models;
using Sarand.BusinessMgr.Web.Portal.Models.Assets;
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.Utils;
using System;
using System.Collections.Generic;
namespace Sarand.BusinessMgr.Web.Portal.Controllers.Instance
{
//=======================================================================
partial class AssetController
{
//-------------------------------------------------------------------
// Definitions.
// Asset data.
private class AssetListData : AjaxResponse_Data.ItemData_Base
{
public String CategoryDesc { get; set; }
public String SerialNumber { get; set; }
public String DescBrief { get; set; }
}
private class AssetItemData : AssetListData
{
public String CategoryId { get; set; }
public String RefExt { get; set; }
public String ItemCaption { get; set; }
public String PurchaseOn_Form { get; set; }
public String AssocIdSupplier { get; set; }
public String SupplierCaption { get; set; }
public String PurchaseInvoiceRef { get; set; }
}
private void PopulateAssetResponseListData(SbmModel_Portal model, AssetListData data, SbmAssetCategory assetCategory, SbmAsset asset)
{
data.Id = SysId.ToString(asset.Id);
data.CategoryDesc = assetCategory.DescBrief;
data.SerialNumber = asset.SerialNumber;
data.DescBrief = asset.DescBrief;
data.StatusCode = asset.StatusCode;
data.StatusDesc = asset.StatusDesc;
}
private void PopulateAssetResponseItemData(SbmModel_Portal model, AssetItemData data, SbmAssetCategory assetCategory, SbmAsset asset)
{
PopulateAssetResponseListData(model, data, assetCategory, asset);
data.CategoryId = SysId.ToString(asset.AssetCategoryId);
data.RefExt = asset.RefExt;
data.ItemCaption = null;
data.PurchaseOn_Form = null;
data.AssocIdSupplier = SysId.ToString(asset.AssocIdSupplier);
data.SupplierCaption = null;
data.PurchaseInvoiceRef = null;
data.Notes = asset.Notes;
}
//-------------------------------------------------------------------
// Get Asset List ajax request.
//-------------------------------------------------------------------
private class AjaxResponse_AssetList : AjaxResponse_Data
{
// Construction.
public AjaxResponse_AssetList(SbmModel_Portal model) : base(model) { }
// Asset list.
public List<AssetListData> Assets { get; } = new List<AssetListData>();
// Reference data.
public List<CodeDesc> Statuses { get; } = new List<CodeDesc>();
public List<CodeDesc> StatusesCreate { get; } = new List<CodeDesc>();
public String StatusCodeDefault { get; set; }
}
//-------------------------------------------------------------------
[HttpPost]
[Route("Assets/Asset/Ajax/GetList")]
[Route("Assets/Asset/Ajax/GetAssets")]
[Route("Assets/Asset/Ajax/GetAssetList")]
public IActionResult Ajax_GetAssetList(Model_Asset model)
{
String fn = GetType().Name + ".Ajax_GetAssetList";
String messageTitle = Localiser["label.assets"];
AjaxResponse_AssetList responseData = new AjaxResponse_AssetList(model);
// Validate session and referrer.
if (!ValidReferer) return Forbid();
if (!ValidUserSession) return GetAjaxResult_InvalidSession(responseData);
RefreshModel(model);
// Reference data - statuses.
foreach (SaStatus status in SbmAsset.ValidStatuses)
{
responseData.Statuses.Add(SacsSystem.GetStatusCode(status), Localiser.GetStatusCaption(status));
if (status != SaStatus.Deleted)
responseData.StatusesCreate.Add(SacsSystem.GetStatusCode(status), Localiser.GetStatusCaption(status));
}
responseData.StatusCodeDefault = SacsSystem.GetStatusCode(SbmAsset.DefaultStatus);
// Item filter.
SbmAsset.Params prmAsset = new SbmAsset.Params();
prmAsset.Search = model.Filter.Search;
prmAsset.StatusCat = model.Filter.GetStatusCat();
model.Paging.ItemTotal = (ulong)AssetMgr.GetAssetCount(prmAsset);
model.PopulatePagingTo(prmAsset.ResultsPaging);
// Item columns.
ColumnDfn colId = AssetMgr.GetAssetColumnDfn(SbmAsset.Column.Id);
ColumnDfn colCategoryDesc = AssetMgr.GetAssetColumnDfn(SbmAsset.Column.Category_DescBrief);
ColumnDfn colSerial = AssetMgr.GetAssetColumnDfn(SbmAsset.Column.SerialNumber);
ColumnDfn colDescBrief = AssetMgr.GetAssetColumnDfn(SbmAsset.Column.DescBrief);
ColumnDfn colStatus = AssetMgr.GetAssetColumnDfn(SbmAsset.Column.Status);
List<ColumnDfn> columns = new List<ColumnDfn>()
{
colId, colCategoryDesc, colSerial, colDescBrief,
colStatus
};
// Query items.
using (DbResultSet resultSet = AssetMgr.ExecuteAssetQuery(columns, prmAsset))
{
while (resultSet != null && resultSet.HasRows && resultSet.NextRecord())
{
NamedValue.Map values = resultSet.GetRecordValues();
AssetListData assetData = new AssetListData()
{
Id = values.GetLong(colId.DataName).ToString(),
CategoryDesc = values.GetString(colCategoryDesc.DataName),
SerialNumber = values.GetString(colSerial.DataName),
DescBrief = values.GetString(colDescBrief.DataName),
StatusCode = values.GetString(colStatus.DataName),
StatusDesc = SacsSystem.GetStatusDescForCode(values.GetString(colStatus.DataName))
};
responseData.Assets.Add(assetData);
}
}
if (model.Paging.IsPaged)
model.Paging.ItemCount = (ushort)responseData.Assets.Count;
// Complete response.
responseData.SetPaging(model);
responseData.Success = true;
return Json(responseData);
}
//-------------------------------------------------------------------
// Get Asset ajax request.
//-------------------------------------------------------------------
private class AjaxResponse_Asset : AjaxResponse_Data
{
// Construction.
public AjaxResponse_Asset(SbmModel_Portal model) : base(model) { }
// Asset details.
public AssetItemData asset { get; } = new AssetItemData();
}
//-------------------------------------------------------------------
[HttpPost]
[Route("Assets/Asset/Ajax/GetItem")]
[Route("Assets/Asset/Ajax/GetAsset")]
public IActionResult Ajax_GetAsset(Model_Asset model)
{
String fn = GetType().Name + ".Ajax_GetAsset";
String messageTitle = Localiser["label.asset"];
AjaxResponse_Asset responseData = new AjaxResponse_Asset(model);
// Validate session and referrer.
if (!ValidReferer) return Forbid();
if (!ValidUserSession) return GetAjaxResult_InvalidSession(responseData);
RefreshModel(model);
// Record disposable items as they are created so all can be disposed when needed.
using (DisposableCollection<SacsHierarchyItem> disposables = new DisposableCollection<SacsHierarchyItem>())
{
// Identify item.
SysId assetId = model.Asset.Id.ToSysId();
if (!SysId.IsValid(assetId))
return Json(responseData.SetMessage(messageTitle, Localiser["msg.error.item.id.not.specified"]));
// Retrieve item.
SbmAsset asset = AssetMgr.GetAssetById(assetId);
disposables.Add(asset);
if (asset == null)
{
SaLogger.Warn($"{SysUtil.Fn} Failed to retrieve asset for Id: '" + assetId + "'");
return Json(responseData.SetMessage(messageTitle, Localiser["msg.error.item.id.not.found"]));
}
// Retrieve category.
SbmAssetCategory assetCategory = AssetMgr.GetAssetCategoryById(asset.AssetCategoryId);
disposables.Add(assetCategory);
if (assetCategory == null)
{
SaLogger.Warn($"{SysUtil.Fn} Failed to retrieve asset category for Id: '" + asset.AssetCategoryId + "'");
return Json(responseData.SetMessage(messageTitle, Localiser["msg.error.system"]));
}
// Construct response.
PopulateAssetResponseItemData(model, responseData.asset, assetCategory, asset);
responseData.Success = true;
}
return Json(responseData);
}
//-------------------------------------------------------------------
// Store Asset ajax request.
//-------------------------------------------------------------------
[HttpPost]
[Route("Assets/Asset/Ajax/StoreItem")]
[Route("Assets/Asset/Ajax/StoreAsset")]
public IActionResult Ajax_StoreAsset(Model_Asset model)
{
String fn = GetType().Name + ".Ajax_StoreAsset";
String messageTitle = Localiser["label.asset"];
AjaxResponse_Asset responseData = new AjaxResponse_Asset(model);
// Validate session and referrer.
if (!ValidReferer) return Forbid();
if (!ValidUserSession) return GetAjaxResult_InvalidSession(responseData);
RefreshModel(model);
// Validate item Id.
SysId assetId = model.Asset.Id.ToSysId();
if (StringUtil.ToBool(model.Asset.Exists) && !SysId.IsValid(assetId))
{
SaLogger.Warn($"{SysUtil.Fn} Asset Id not supplied for existing item.");
return Json(responseData.SetMessage(messageTitle, Localiser["msg.error.system"]));
}
if (!StringUtil.ToBool(model.Asset.Exists) && SysId.IsValid(assetId))
{
SaLogger.Warn($"{SysUtil.Fn} Asset Id supplied for new item.");
return Json(responseData.SetMessage(messageTitle, Localiser["msg.error.system"]));
}
// Validate item properties.
model.ValidateAssetData(responseData);
if (responseData.HasControlErrors())
return Json(responseData);
// Record disposable items as they are created so all can be disposed when needed.
using (DisposableCollection<SacsHierarchyItem> disposables = new DisposableCollection<SacsHierarchyItem>())
{
// Retrieve existing item.
SbmAsset asset = AssetMgr.GetAssetById(assetId);
disposables.Add(asset);
if (asset == null && SysId.IsValid(assetId))
{
SaLogger.Warn($"{SysUtil.Fn} Failed to retrieve Asset for Id: '" + model.Asset.Id + "'");
return Json(responseData.SetMessage(messageTitle, Localiser["msg.error.item.id.not.found"]));
}
// Retrieve category.
SbmAssetCategory assetCategory = null;
if (asset != null)
{
assetCategory = AssetMgr.GetAssetCategoryById(asset.AssetCategoryId);
disposables.Add(assetCategory);
if (assetCategory == null)
{
SaLogger.Warn($"{SysUtil.Fn} Failed to retrieve Asset Category for existing Asset. Id: '" + asset.AssetCategoryId + "'");
return Json(responseData.SetMessage(messageTitle, Localiser["msg.error.system"]));
}
}
else
{
assetCategory = AssetMgr.GetAssetCategoryById(model.Asset.CategoryId.ToSysId());
disposables.Add(assetCategory);
if (assetCategory == null)
{
SaLogger.Warn($"{SysUtil.Fn} Failed to retrieve Asset Category for new Asset. Id: '" + model.Asset.CategoryId + "'");
return Json(responseData.SetMessage(messageTitle, Localiser["msg.error.system"]));
}
}
// Create new item if required.
if (asset == null)
{
if (assetCategory == null || !SysId.IsValid(assetCategory.Id))
{
// Should be picked up by model validation unless form doesn't include Asset Category.
SaLogger.Warn($"{SysUtil.Fn} Asset Category not specified for new Asset.");
return Json(responseData.SetMessage(messageTitle, Localiser["msg.error.system"]));
}
asset = AssetMgr.CreateAsset(assetCategory.Id);
}
// Populate item properties.
if (model.Asset.SerialNumber != null)
asset.SerialNumber = model.Asset.SerialNumber;
if (model.Asset.DescBrief != null)
asset.DescBrief = model.Asset.DescBrief;
if (model.Asset.StatusCode != null)
asset.Status = model.Asset.GetStatus();
if (model.Asset.Notes != null)
asset.Notes = model.Asset.Notes;
// Store item.
if (!SysId.IsValid(asset.Id))
{
if (!AssetMgr.InsertAsset(asset, DbUpdateInfo))
{
SaLogger.Warn($"{SysUtil.Fn} Failed to insert Asset: " + AssetMgr.ErrorStateMessage);
return Json(responseData.SetMessage(messageTitle, Localiser["msg.error.item.insert.failed"]));
}
}
else
{
if (!AssetMgr.UpdateAsset(asset, DbUpdateInfo))
{
SaLogger.Warn($"{SysUtil.Fn} Failed to update Asset [Id:" + SysId.ToString(asset.Id) + "]: " + AssetMgr.ErrorStateMessage);
return Json(responseData.SetMessage(messageTitle, Localiser["msg.error.item.update.failed"]));
}
}
// Construct response.
PopulateAssetResponseItemData(model, responseData.asset, assetCategory, asset);
responseData.Success = true;
}
return Json(responseData);
}
//-------------------------------------------------------------------
}
//=======================================================================
}
@@ -0,0 +1,49 @@
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Logging;
using Sarand.BusinessMgr.Net.Core.Web.App;
using Sarand.BusinessMgr.Web.Portal.Models;
using Sarand.BusinessMgr.Web.Portal.Models.Assets;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Threading.Tasks;
namespace Sarand.BusinessMgr.Web.Portal.Controllers.Instance
{
//======================================================================
public partial class AssetController : SbmController_Portal
{
//-------------------------------------------------------------------
// Data members.
//-------------------------------------------------------------------
// Construction.
public AssetController(SbmLocaliser localiser, ILogger<AssetController> logger) :
base(localiser, logger)
{
}
//-------------------------------------------------------------------
[Route("Assets/Asset")]
public IActionResult Index()
{
if (!ValidUserSession)
AutoLoginUser();
// Redirect to sign-in if not signed-in.
if (!ValidUserSession)
{
ClearKeepLoggedInCookie();
return RedirectToAction("", "Security");
}
// Asset page.
Model_Asset model = new Model_Asset(Localiser);
return View("Views/Assets/Asset/Index.cshtml", model);
}
//-------------------------------------------------------------------
}
//======================================================================
}
@@ -0,0 +1,295 @@
using Microsoft.AspNetCore.Mvc;
using Sarand.BusinessMgr.Net.Standard.Base.App;
using Sarand.BusinessMgr.Net.Standard.Base.Bus;
using Sarand.BusinessMgr.Net.Standard.Base.Bus.Control;
using Sarand.BusinessMgr.Web.Portal.Models;
using Sarand.BusinessMgr.Web.Portal.Models.Assets;
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.Utils;
using System;
using System.Collections.Generic;
using System.Linq;
namespace Sarand.BusinessMgr.Web.Portal.Controllers.Instance
{
//=======================================================================
partial class VehicleClassController
{
//-------------------------------------------------------------------
// Definitions.
// VehicleClass data.
private class VehicleClassListData : AjaxResponse_Data.ItemData_Base
{
public String MakeDesc { get; set; }
public String Model { get; set; }
public String Derivative { get; set; }
public String DescBrief { get; set; }
public long? VehicleCount { get; set; }
}
private class VehicleClassItemData : VehicleClassListData
{
public String MakeCode { get; set; }
}
private void PopulateVehicleClassResponseListData(SbmModel_Portal model, VehicleClassListData data, SbmVehicleClass vehicleClass)
{
data.Id = SysId.ToString(vehicleClass.Id);
data.MakeDesc = GetMakeDesc(vehicleClass.MakeCode);
data.Model = vehicleClass.Model;
data.Derivative = vehicleClass.Derivative;
data.DescBrief = vehicleClass.DescBrief;
data.StatusCode = vehicleClass.StatusCode;
data.StatusDesc = vehicleClass.StatusDesc;
}
private String GetMakeDesc(String makeCode)
{
return Internals.GetCodesForCodeType(Sbm.ApplCodeType.VehicleMake, false).FirstOrDefault(vm => vm.Code.EqualsCI(makeCode))?.Description ?? makeCode;
}
private void PopulateVehicleClassResponseItemData(SbmModel_Portal model, VehicleClassItemData data, SbmVehicleClass vehicleClass)
{
PopulateVehicleClassResponseListData(model, data, vehicleClass);
data.MakeCode = vehicleClass.MakeCode;
data.Notes = vehicleClass.Notes;
}
//-------------------------------------------------------------------
// Get VehicleClass List ajax request.
//-------------------------------------------------------------------
private class AjaxResponse_VehicleClassList : AjaxResponse_Data
{
// Construction.
public AjaxResponse_VehicleClassList(SbmModel_Portal model) : base(model) { }
// VehicleClass list.
public List<VehicleClassListData> VehicleClasses { get; } = new List<VehicleClassListData>();
// Reference data.
public List<CodeDesc> Statuses { get; } = new List<CodeDesc>();
public List<CodeDesc> StatusesCreate { get; } = new List<CodeDesc>();
public String StatusCodeDefault { get; set; }
}
//-------------------------------------------------------------------
[HttpPost]
[Route("Assets/VehicleClass/Ajax/GetList")]
[Route("Assets/VehicleClass/Ajax/GetVehicleClasses")]
[Route("Assets/VehicleClass/Ajax/GetVehicleClassList")]
public IActionResult Ajax_GetVehicleClassList(Model_VehicleClass model)
{
String fn = GetType().Name + ".Ajax_GetVehicleClassList";
String messageTitle = Localiser["caption.vehicle.classes"];
AjaxResponse_VehicleClassList responseData = new AjaxResponse_VehicleClassList(model);
// Validate session and referrer.
if (!ValidReferer) return Forbid();
if (!ValidUserSession) return GetAjaxResult_InvalidSession(responseData);
RefreshModel(model);
// Reference data - statuses.
foreach (SaStatus status in SbmVehicleClass.ValidStatuses)
{
responseData.Statuses.Add(SacsSystem.GetStatusCode(status), Localiser.GetStatusCaption(status));
if (status != SaStatus.Deleted)
responseData.StatusesCreate.Add(SacsSystem.GetStatusCode(status), Localiser.GetStatusCaption(status));
}
responseData.StatusCodeDefault = SacsSystem.GetStatusCode(SbmVehicleClass.DefaultStatus);
// Item filter.
SbmVehicleClass.Params prmVehicleClass = new SbmVehicleClass.Params();
prmVehicleClass.Search = model.Filter.Search;
prmVehicleClass.StatusCat = model.Filter.GetStatusCat();
model.Paging.ItemTotal = (ulong)AssetMgr.GetVehicleClassCount(prmVehicleClass);
model.PopulatePagingTo(prmVehicleClass.ResultsPaging);
// Item columns.
ColumnDfn colId = AssetMgr.GetVehicleClassColumnDfn(SbmVehicleClass.Column.Id);
ColumnDfn colMakeCode = AssetMgr.GetVehicleClassColumnDfn(SbmVehicleClass.Column.MakeCode);
ColumnDfn colModel = AssetMgr.GetVehicleClassColumnDfn(SbmVehicleClass.Column.Model);
ColumnDfn colDerivative = AssetMgr.GetVehicleClassColumnDfn(SbmVehicleClass.Column.Derivative);
ColumnDfn colDescBrief = AssetMgr.GetVehicleClassColumnDfn(SbmVehicleClass.Column.DescBrief);
ColumnDfn colVehicleCount = AssetMgr.GetVehicleClassColumnDfn(SbmVehicleClass.Column.VehicleCount);
ColumnDfn colStatus = AssetMgr.GetVehicleClassColumnDfn(SbmVehicleClass.Column.Status);
List<ColumnDfn> columns = new List<ColumnDfn>()
{
colId, colMakeCode, colModel, colDerivative, colDescBrief,
colVehicleCount, colStatus
};
// Query items.
using (DbResultSet resultSet = AssetMgr.ExecuteVehicleClassQuery(columns, prmVehicleClass))
{
while (resultSet != null && resultSet.HasRows && resultSet.NextRecord())
{
NamedValue.Map values = resultSet.GetRecordValues();
VehicleClassListData vehicleClassData = new VehicleClassListData()
{
Id = values.GetLong(colId.DataName).ToString(),
MakeDesc = GetMakeDesc(values.GetString(colMakeCode.DataName)),
Model = values.GetString(colModel.DataName),
Derivative = values.GetString(colDerivative.DataName),
DescBrief = values.GetString(colDescBrief.DataName),
StatusCode = values.GetString(colStatus.DataName),
StatusDesc = SacsSystem.GetStatusDescForCode(values.GetString(colStatus.DataName))
};
long vehicleCount = values.GetLong(colVehicleCount.DataName);
if (vehicleCount > 0)
vehicleClassData.VehicleCount = vehicleCount;
responseData.VehicleClasses.Add(vehicleClassData);
}
}
if (model.Paging.IsPaged)
model.Paging.ItemCount = (ushort)responseData.VehicleClasses.Count;
// Complete response.
responseData.SetPaging(model);
responseData.Success = true;
return Json(responseData);
}
//-------------------------------------------------------------------
// Get VehicleClass ajax request.
//-------------------------------------------------------------------
private class AjaxResponse_VehicleClass : AjaxResponse_Data
{
// Construction.
public AjaxResponse_VehicleClass(SbmModel_Portal model) : base(model) { }
// VehicleClass details.
public VehicleClassItemData vehicleClass { get; } = new VehicleClassItemData();
}
//-------------------------------------------------------------------
[HttpPost]
[Route("Assets/VehicleClass/Ajax/GetItem")]
[Route("Assets/VehicleClass/Ajax/GetVehicleClass")]
public IActionResult Ajax_GetVehicleClass(Model_VehicleClass model)
{
String fn = GetType().Name + ".Ajax_GetVehicleClass";
String messageTitle = Localiser["caption.vehicle.class"];
AjaxResponse_VehicleClass responseData = new AjaxResponse_VehicleClass(model);
// Validate session and referrer.
if (!ValidReferer) return Forbid();
if (!ValidUserSession) return GetAjaxResult_InvalidSession(responseData);
RefreshModel(model);
// Record disposable items as they are created so all can be disposed when needed.
using (DisposableCollection<SacsHierarchyItem> disposables = new DisposableCollection<SacsHierarchyItem>())
{
// Identify item.
SysId vehicleClassId = model.VehicleClass.Id.ToSysId();
if (!SysId.IsValid(vehicleClassId))
return Json(responseData.SetMessage(messageTitle, Localiser["msg.error.item.id.not.specified"]));
// Retrieve item.
SbmVehicleClass vehicleClass = AssetMgr.GetVehicleClassById(vehicleClassId);
disposables.Add(vehicleClass);
if (vehicleClass == null)
{
SaLogger.Warn($"{SysUtil.Fn} Failed to retrieve vehicleClass for Id: '" + vehicleClassId + "'");
return Json(responseData.SetMessage(messageTitle, Localiser["msg.error.item.id.not.found"]));
}
// Construct response.
PopulateVehicleClassResponseItemData(model, responseData.vehicleClass, vehicleClass);
responseData.Success = true;
}
return Json(responseData);
}
//-------------------------------------------------------------------
// Store VehicleClass ajax request.
//-------------------------------------------------------------------
[HttpPost]
[Route("Assets/VehicleClass/Ajax/StoreItem")]
[Route("Assets/VehicleClass/Ajax/StoreVehicleClass")]
public IActionResult Ajax_StoreVehicleClass(Model_VehicleClass model)
{
String fn = GetType().Name + ".Ajax_StoreVehicleClass";
String messageTitle = Localiser["caption.vehicle.class"];
AjaxResponse_VehicleClass responseData = new AjaxResponse_VehicleClass(model);
// Validate session and referrer.
if (!ValidReferer) return Forbid();
if (!ValidUserSession) return GetAjaxResult_InvalidSession(responseData);
RefreshModel(model);
// Validate item Id.
SysId vehicleClassId = model.VehicleClass.Id.ToSysId();
if (StringUtil.ToBool(model.VehicleClass.Exists) && !SysId.IsValid(vehicleClassId))
{
SaLogger.Warn($"{SysUtil.Fn} VehicleClass Id not supplied for existing item.");
return Json(responseData.SetMessage(messageTitle, Localiser["msg.error.system"]));
}
if (!StringUtil.ToBool(model.VehicleClass.Exists) && SysId.IsValid(vehicleClassId))
{
SaLogger.Warn($"{SysUtil.Fn} VehicleClass Id supplied for new item.");
return Json(responseData.SetMessage(messageTitle, Localiser["msg.error.system"]));
}
// Validate item properties.
model.ValidateVehicleClassData(responseData);
if (responseData.HasControlErrors())
return Json(responseData);
// Record disposable items as they are created so all can be disposed when needed.
using (DisposableCollection<SacsHierarchyItem> disposables = new DisposableCollection<SacsHierarchyItem>())
{
// Retrieve existing item.
SbmVehicleClass vehicleClass = AssetMgr.GetVehicleClassById(vehicleClassId);
disposables.Add(vehicleClass);
if (vehicleClass == null && SysId.IsValid(vehicleClassId))
{
SaLogger.Warn($"{SysUtil.Fn} Failed to retrieve Vehicle Class for Id: '" + model.VehicleClass.Id + "'");
return Json(responseData.SetMessage(messageTitle, Localiser["msg.error.item.id.not.found"]));
}
// Create new item if required.
if (vehicleClass == null)
{
vehicleClass = AssetMgr.CreateVehicleClass();
}
// Populate item properties.
if (model.VehicleClass.MakeCode != null)
vehicleClass.MakeCode = model.VehicleClass.MakeCode;
if (model.VehicleClass.Model != null)
vehicleClass.Model = model.VehicleClass.Model;
if (model.VehicleClass.Derivative != null)
vehicleClass.Derivative = model.VehicleClass.Derivative;
if (model.VehicleClass.DescBrief != null)
vehicleClass.DescBrief = model.VehicleClass.DescBrief;
if (model.VehicleClass.StatusCode != null)
vehicleClass.Status = model.VehicleClass.GetStatus();
if (model.VehicleClass.Notes != null)
vehicleClass.Notes = model.VehicleClass.Notes;
// Store item.
if (!SysId.IsValid(vehicleClass.Id))
{
if (!AssetMgr.InsertVehicleClass(vehicleClass, DbUpdateInfo))
{
SaLogger.Warn($"{SysUtil.Fn} Failed to insert Vehicle Class: " + AssetMgr.ErrorStateMessage);
return Json(responseData.SetMessage(messageTitle, Localiser["msg.error.item.insert.failed"]));
}
}
else
{
if (!AssetMgr.UpdateVehicleClass(vehicleClass, DbUpdateInfo))
{
SaLogger.Warn($"{SysUtil.Fn} Failed to update Vehicle Class [Id:" + SysId.ToString(vehicleClass.Id) + "]: " + AssetMgr.ErrorStateMessage);
return Json(responseData.SetMessage(messageTitle, Localiser["msg.error.item.update.failed"]));
}
}
// Construct response.
PopulateVehicleClassResponseItemData(model, responseData.vehicleClass, vehicleClass);
responseData.Success = true;
}
return Json(responseData);
}
//-------------------------------------------------------------------
}
//=======================================================================
}
@@ -0,0 +1,49 @@
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Logging;
using Sarand.BusinessMgr.Net.Core.Web.App;
using Sarand.BusinessMgr.Web.Portal.Models;
using Sarand.BusinessMgr.Web.Portal.Models.Assets;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Threading.Tasks;
namespace Sarand.BusinessMgr.Web.Portal.Controllers.Instance
{
//======================================================================
public partial class VehicleClassController : SbmController_Portal
{
//-------------------------------------------------------------------
// Data members.
//-------------------------------------------------------------------
// Construction.
public VehicleClassController(SbmLocaliser localiser, ILogger<VehicleClassController> logger) :
base(localiser, logger)
{
}
//-------------------------------------------------------------------
[Route("Assets/VehicleClass")]
public IActionResult Index()
{
if (!ValidUserSession)
AutoLoginUser();
// Redirect to sign-in if not signed-in.
if (!ValidUserSession)
{
ClearKeepLoggedInCookie();
return RedirectToAction("", "Security");
}
// VehicleClass page.
Model_VehicleClass model = new Model_VehicleClass(Localiser);
return View("Views/Assets/VehicleClass/Index.cshtml", model);
}
//-------------------------------------------------------------------
}
//======================================================================
}
@@ -1,7 +1,7 @@
using Microsoft.AspNetCore.Mvc;
using Sarand.BusinessMgr.Net.Core.Web.App;
using Sarand.BusinessMgr.Net.Core.Web.Helpers;
using Sarand.BusinessMgr.Net.Core.Web.Portal.Models;
using Sarand.BusinessMgr.Web.Portal.Models;
using Sarand.BusinessMgr.Net.Standard.Base.App;
using Sarand.BusinessMgr.Net.Standard.Base.Bus;
using Sarand.BusinessMgr.Net.Standard.Base.Consoles;
@@ -22,7 +22,7 @@ using System.Linq;
using System.Threading.Tasks;
using System.Web;
namespace Sarand.BusinessMgr.Net.Core.Web.Portal.Controllers
namespace Sarand.BusinessMgr.Web.Portal.Controllers
{
//=======================================================================
// Portal Association Controller - Ajax actions.
@@ -114,7 +114,7 @@ namespace Sarand.BusinessMgr.Net.Core.Web.Portal.Controllers
// Item filter.
SbmAssociation.Params prmAssociation = new SbmAssociation.Params();
prmAssociation.AffiliateIdOwner = SessionData.Organisation.AffiliateId;
prmAssociation.AffiliateIdOwner = SessionData_Portal.Organisation.AffiliateId;
prmAssociation.Search = model.Filter.AssociationSearch;
prmAssociation.AssociationType = model.GetFilterAssociationType();
prmAssociation.StatusCat = SbmAssociation.StatusCat.NotDeleted;
@@ -276,7 +276,7 @@ namespace Sarand.BusinessMgr.Net.Core.Web.Portal.Controllers
// Create new item if required.
if (association == null)
{
association = AccountMgr.CreateAssociation(SessionData.Organisation.AffiliateId, model.GetAssociationType(), model.AffiliateIdTarget.ToSysId());
association = AccountMgr.CreateAssociation(SessionData_Portal.Organisation.AffiliateId, model.GetAssociationType(), model.AffiliateIdTarget.ToSysId());
}
// Populate item properties.
@@ -368,7 +368,7 @@ namespace Sarand.BusinessMgr.Net.Core.Web.Portal.Controllers
if (!AccountMgr.InsertAssociation(association, DbUpdateInfo))
{
SaLogger.Warn($"{SysUtil.Fn} Failed to insert association: " + AccountMgr.ErrorStateMessage);
return Json(responseData.SetMessage(messageTitle, Localiser["msg.error.item.insert.failed"] + "\n\n" + AccountMgr.ErrorStateMessage));
return Json(responseData.SetMessage(messageTitle, Localiser["msg.error.item.insert.failed"]));
}
responseData.SetNotification(messageTitle, Localiser["msg.item.created"]);
}
@@ -377,7 +377,7 @@ namespace Sarand.BusinessMgr.Net.Core.Web.Portal.Controllers
if (!AccountMgr.UpdateAssociation(association, DbUpdateInfo))
{
SaLogger.Warn($"{SysUtil.Fn} Failed to update association [Id:" + SysId.ToString(association.Id) + "]: " + AccountMgr.ErrorStateMessage);
return Json(responseData.SetMessage(messageTitle, Localiser["msg.error.item.update.failed"] + "\n\n" + AccountMgr.ErrorStateMessage));
return Json(responseData.SetMessage(messageTitle, Localiser["msg.error.item.update.failed"]));
}
responseData.SetNotification(messageTitle, Localiser["msg.item.updated"]);
}
@@ -2,7 +2,7 @@
using Microsoft.Extensions.Logging;
using Sarand.BusinessMgr.Net.Core.Web.App;
using Sarand.BusinessMgr.Net.Core.Web.Helpers;
using Sarand.BusinessMgr.Net.Core.Web.Portal.Models;
using Sarand.BusinessMgr.Web.Portal.Models;
using Sarand.BusinessMgr.Net.Standard.Base.App;
using Sarand.BusinessMgr.Net.Standard.Base.Bus;
using Sarand.BusinessMgr.Net.Standard.Base.Mgr;
@@ -16,7 +16,7 @@ using System.Diagnostics;
using System.Linq;
using System.Threading.Tasks;
namespace Sarand.BusinessMgr.Net.Core.Web.Portal.Controllers
namespace Sarand.BusinessMgr.Web.Portal.Controllers
{
//=======================================================================
public partial class AssociationController : SbmController_Portal
@@ -36,7 +36,14 @@ namespace Sarand.BusinessMgr.Net.Core.Web.Portal.Controllers
public IActionResult Index()
{
if (!ValidUserSession)
return RedirectToAction("", "");
AutoLoginUser();
// Redirect to sign-in if not signed-in.
if (!ValidUserSession)
{
ClearKeepLoggedInCookie();
return RedirectToAction("", "Security");
}
// Association page.
Model_Association model = new Model_Association(Localiser);
@@ -0,0 +1,328 @@
using Microsoft.AspNetCore.Mvc;
using NPOI.OpenXmlFormats.Dml.Diagram;
using Sarand.BusinessMgr.Net.Standard.Base.App;
using Sarand.BusinessMgr.Net.Standard.Base.Bus;
using Sarand.BusinessMgr.Net.Standard.Base.Bus.Control;
using Sarand.BusinessMgr.Web.Portal.Models;
using Sarand.BusinessMgr.Web.Portal.Models.Codes;
using Sarand.Net.Standard.Base.App;
using Sarand.Net.Standard.Base.Bus;
using Sarand.Net.Standard.Base.CodeTypes;
using Sarand.Net.Standard.Base.Core;
using Sarand.Net.Standard.Base.Dbas;
using Sarand.Net.Standard.Base.Utils;
using System;
using System.Collections.Generic;
namespace Sarand.BusinessMgr.Web.Portal.Controllers.Instance
{
//=======================================================================
partial class CodeController
{
//-------------------------------------------------------------------
// Definitions.
// Code data.
private class CodeListData : AjaxResponse_Data.ItemData_Base
{
public String TypeCode { get; set; }
public String TypeName { get; set; }
public String Code { get; set; }
public String Description { get; set; }
}
private class CodeItemData : CodeListData
{
public String CodeTypeId { get; set; }
public ushort? Sequence { get; set; }
public String DataValue { get; set; }
}
private void PopulateCodeResponseListData(SbmModel_Portal model, CodeListData data, SaCodeType codeType, SaCode code)
{
data.Id = SysId.ToString(code.Id);
data.TypeCode = codeType.Code;
data.TypeName = codeType.Name;
data.Code = code.Code;
data.Description = code.Description;
data.StatusCode = code.StatusCode;
data.StatusDesc = code.StatusDesc;
}
private void PopulateCodeResponseItemData(SbmModel_Portal model, CodeItemData data, SaCodeType codeType, SaCode code)
{
PopulateCodeResponseListData(model, data, codeType, code);
data.CodeTypeId = SysId.ToString(code.CodeTypeId);
if (code.Sequence > 0)
data.Sequence = code.Sequence;
data.DataValue = code.DataValue;
data.Notes = code.Notes;
}
//-------------------------------------------------------------------
// Get Code List ajax request.
//-------------------------------------------------------------------
private class AjaxResponse_CodeList : AjaxResponse_Data
{
// Construction.
public AjaxResponse_CodeList(SbmModel_Portal model) : base(model) { }
// Code list.
public List<CodeListData> Codes { get; } = new List<CodeListData>();
// Reference data.
public List<CodeDesc> Statuses { get; } = new List<CodeDesc>();
public List<CodeDesc> StatusesCreate { get; } = new List<CodeDesc>();
public String StatusCodeDefault { get; set; }
}
//-------------------------------------------------------------------
[HttpPost]
[Route("Codes/Code/Ajax/GetList")]
[Route("Codes/Code/Ajax/GetCodes")]
[Route("Codes/Code/Ajax/GetCodeList")]
public IActionResult Ajax_GetCodeList(Model_Code model)
{
String fn = GetType().Name + ".Ajax_GetCodeList";
String messageTitle = Localiser["label.codes"];
AjaxResponse_CodeList responseData = new AjaxResponse_CodeList(model);
// Validate session and referrer.
if (!ValidReferer) return Forbid();
if (!ValidUserSession) return GetAjaxResult_InvalidSession(responseData);
RefreshModel(model);
// Reference data - statuses.
foreach (SaStatus status in SaCode.ValidStatuses)
{
responseData.Statuses.Add(SacsSystem.GetStatusCode(status), Localiser.GetStatusCaption(status));
if (status != SaStatus.Deleted)
responseData.StatusesCreate.Add(SacsSystem.GetStatusCode(status), Localiser.GetStatusCaption(status));
}
responseData.StatusCodeDefault = SacsSystem.GetStatusCode(SaCode.DefaultStatus);
// Item filter.
SaCode.Params prmCode = new SaCode.Params();
if (!StringUtil.IsBlank(model.Filter.CodeTypeCode))
prmCode.CodeTypeCodes.Add(model.Filter.CodeTypeCode);
prmCode.Search = model.Filter.Search;
prmCode.StatusCat = model.Filter.GetStatusCat();
model.Paging.ItemTotal = (ulong)CodeTypeConsole.GetCodeCount(prmCode);
model.PopulatePagingTo(prmCode.ResultsPaging);
// Item columns.
ColumnDfn colId = CodeTypeConsole.GetCodeColumnDfn(SaCode.Column.Id);
ColumnDfn colTypeCode = CodeTypeConsole.GetCodeColumnDfn(SaCode.Column.CodeType_Code);
ColumnDfn colTypeName = CodeTypeConsole.GetCodeColumnDfn(SaCode.Column.CodeType_Name);
ColumnDfn colCode = CodeTypeConsole.GetCodeColumnDfn(SaCode.Column.Code);
ColumnDfn colDesc = CodeTypeConsole.GetCodeColumnDfn(SaCode.Column.Description);
ColumnDfn colStatus = CodeTypeConsole.GetCodeColumnDfn(SaCode.Column.Status);
List<ColumnDfn> columns = new List<ColumnDfn>()
{
colId, colTypeCode, colTypeName, colCode, colDesc,
colStatus
};
prmCode.SortColumns.Add(colTypeName);
prmCode.SortColumns.Add(colDesc);
// Query items.
using (DbResultSet resultSet = CodeTypeConsole.ExecuteCodeQuery(columns, prmCode))
{
while (resultSet != null && resultSet.HasRows && resultSet.NextRecord())
{
NamedValue.Map values = resultSet.GetRecordValues();
CodeListData codeData = new CodeListData()
{
Id = values.GetLong(colId.DataName).ToString(),
TypeCode = values.GetString(colTypeCode.DataName),
TypeName = values.GetString(colTypeName.DataName),
Code = values.GetString(colCode.DataName),
Description = values.GetString(colDesc.DataName),
StatusCode = values.GetString(colStatus.DataName),
StatusDesc = SacsSystem.GetStatusDescForCode(values.GetString(colStatus.DataName))
};
responseData.Codes.Add(codeData);
}
}
if (model.Paging.IsPaged)
model.Paging.ItemCount = (ushort)responseData.Codes.Count;
// Complete response.
responseData.SetPaging(model);
responseData.Success = true;
return Json(responseData);
}
//-------------------------------------------------------------------
// Get Code ajax request.
//-------------------------------------------------------------------
private class AjaxResponse_Code : AjaxResponse_Data
{
// Construction.
public AjaxResponse_Code(SbmModel_Portal model) : base(model) { }
// Code details.
public CodeItemData code { get; } = new CodeItemData();
}
//-------------------------------------------------------------------
[HttpPost]
[Route("Codes/Code/Ajax/GetItem")]
[Route("Codes/Code/Ajax/GetCode")]
public IActionResult Ajax_GetCode(Model_Code model)
{
String fn = GetType().Name + ".Ajax_GetCode";
String messageTitle = Localiser["label.code"];
AjaxResponse_Code responseData = new AjaxResponse_Code(model);
// Validate session and referrer.
if (!ValidReferer) return Forbid();
if (!ValidUserSession) return GetAjaxResult_InvalidSession(responseData);
RefreshModel(model);
// Record disposable items as they are created so all can be disposed when needed.
using (DisposableCollection<SacsHierarchyItem> disposables = new DisposableCollection<SacsHierarchyItem>())
{
// Identify item.
SysId codeId = model.Code.Id.ToSysId();
if (!SysId.IsValid(codeId))
return Json(responseData.SetMessage(messageTitle, Localiser["msg.error.item.id.not.specified"]));
// Retrieve item.
SaCode code = CodeTypeConsole.GetCodeById(codeId);
disposables.Add(code);
if (code == null)
{
SaLogger.Warn($"{SysUtil.Fn} Failed to retrieve code for Id: '" + codeId + "'");
return Json(responseData.SetMessage(messageTitle, Localiser["msg.error.item.id.not.found"]));
}
// Retrieve code type.
SaCodeType codeType = CodeTypeConsole.GetCodeTypeById(code.CodeTypeId);
disposables.Add(codeType);
if (codeType == null)
{
SaLogger.Warn($"{SysUtil.Fn} Failed to retrieve code type for Id: '" + code.CodeTypeId + "'");
return Json(responseData.SetMessage(messageTitle, Localiser["msg.error.system"]));
}
// Construct response.
PopulateCodeResponseItemData(model, responseData.code, codeType, code);
responseData.Success = true;
}
return Json(responseData);
}
//-------------------------------------------------------------------
// Store Code ajax request.
//-------------------------------------------------------------------
[HttpPost]
[Route("Codes/Code/Ajax/StoreItem")]
[Route("Codes/Code/Ajax/StoreCode")]
public IActionResult Ajax_StoreCode(Model_Code model)
{
String fn = GetType().Name + ".Ajax_StoreCode";
String messageTitle = Localiser["label.code"];
AjaxResponse_Code responseData = new AjaxResponse_Code(model);
// Validate session and referrer.
if (!ValidReferer) return Forbid();
if (!ValidUserSession) return GetAjaxResult_InvalidSession(responseData);
RefreshModel(model);
// Validate item Id.
SysId codeId = model.Code.Id.ToSysId();
if (StringUtil.ToBool(model.Code.Exists) && !SysId.IsValid(codeId))
{
SaLogger.Warn($"{SysUtil.Fn} Code Id not supplied for existing item.");
return Json(responseData.SetMessage(messageTitle, Localiser["msg.error.system"]));
}
if (!StringUtil.ToBool(model.Code.Exists) && SysId.IsValid(codeId))
{
SaLogger.Warn($"{SysUtil.Fn} Code Id supplied for new item.");
return Json(responseData.SetMessage(messageTitle, Localiser["msg.error.system"]));
}
// Validate item properties.
model.ValidateCodeData(responseData);
if (responseData.HasControlErrors())
return Json(responseData);
// Record disposable items as they are created so all can be disposed when needed.
using (DisposableCollection<SacsHierarchyItem> disposables = new DisposableCollection<SacsHierarchyItem>())
{
// Retrieve existing item.
SaCode code = CodeTypeConsole.GetCodeById(codeId);
disposables.Add(code);
if (code == null && SysId.IsValid(codeId))
{
SaLogger.Warn($"{SysUtil.Fn} Failed to retrieve Code Type for Id: '" + model.Code.Id + "'");
return Json(responseData.SetMessage(messageTitle, Localiser["msg.error.item.id.not.found"]));
}
// Retrieve code type.
SaCodeType codeType = null;
if (code != null)
{
codeType = CodeTypeConsole.GetCodeTypeById(code.CodeTypeId);
disposables.Add(codeType);
if (codeType == null)
{
SaLogger.Warn($"{SysUtil.Fn} Failed to retrieve Code Type for existing Code. Id: '" + code.CodeTypeId + "'");
return Json(responseData.SetMessage(messageTitle, Localiser["msg.error.system"]));
}
}
else
{
codeType = CodeTypeConsole.GetCodeTypeById(model.Code.CodeTypeId.ToSysId());
disposables.Add(codeType);
if (codeType == null)
{
SaLogger.Warn($"{SysUtil.Fn} Failed to retrieve Code Type for new Code. Id: '" + model.Code.CodeTypeId + "'");
return Json(responseData.SetMessage(messageTitle, Localiser["msg.error.system"]));
}
}
// Create new item if required.
if (code == null)
{
if (codeType == null || !SysId.IsValid(codeType.Id))
{
// Should be picked up by model validation unless form doesn't include Code Type.
SaLogger.Warn($"{SysUtil.Fn} Code Type not specified for new Code.");
return Json(responseData.SetMessage(messageTitle, Localiser["msg.error.system"]));
}
code = CodeTypeConsole.CreateCode(codeType.Id, model.Code.Code);
}
// Populate item properties.
if (model.Code.Description != null)
code.Description = model.Code.Description;
if (model.Code.StatusCode != null)
code.Status = model.Code.GetStatus();
if (model.Code.Notes != null)
code.Notes = model.Code.Notes;
// Store item.
if (!SysId.IsValid(code.Id))
{
if (!CodeTypeConsole.InsertCode(code, DbUpdateInfo))
{
SaLogger.Warn($"{SysUtil.Fn} Failed to insert Code: " + CodeTypeConsole.ErrorStateMessage);
return Json(responseData.SetMessage(messageTitle, Localiser["msg.error.item.insert.failed"]));
}
}
else
{
if (!CodeTypeConsole.UpdateCode(code, DbUpdateInfo))
{
SaLogger.Warn($"{SysUtil.Fn} Failed to update Code [Id:" + SysId.ToString(code.Id) + "]: " + CodeTypeConsole.ErrorStateMessage);
return Json(responseData.SetMessage(messageTitle, Localiser["msg.error.item.update.failed"]));
}
}
// Construct response.
PopulateCodeResponseItemData(model, responseData.code, codeType, code);
responseData.Success = true;
}
return Json(responseData);
}
//-------------------------------------------------------------------
}
//=======================================================================
}
@@ -0,0 +1,52 @@
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Logging;
using Sarand.BusinessMgr.Net.Core.Web.App;
using Sarand.BusinessMgr.Web.Portal.Models;
using Sarand.BusinessMgr.Web.Portal.Models.Codes;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Threading.Tasks;
namespace Sarand.BusinessMgr.Web.Portal.Controllers.Instance
{
//======================================================================
public partial class CodeController : SbmController_Portal
{
//-------------------------------------------------------------------
// Data members.
//-------------------------------------------------------------------
// Construction.
public CodeController(SbmLocaliser localiser, ILogger<CodeController> logger) :
base(localiser, logger)
{
}
//-------------------------------------------------------------------
[Route("Codes/Code")]
public IActionResult Index(Model_Code model)
{
if (!ValidUserSession)
AutoLoginUser();
// Redirect to sign-in if not signed-in.
if (!ValidUserSession)
{
ClearKeepLoggedInCookie();
return RedirectToAction("", "Security");
}
// Prepare model.
RefreshModel(model);
model.PopulateCodeTypes();
// Code page.
return View("Views/Codes/Code/Index.cshtml", model);
}
//-------------------------------------------------------------------
}
//======================================================================
}
@@ -0,0 +1,295 @@
using Microsoft.AspNetCore.Mvc;
using NPOI.SS.Formula.Functions;
using Sarand.BusinessMgr.Net.Standard.Base.App;
using Sarand.BusinessMgr.Net.Standard.Base.Bus;
using Sarand.BusinessMgr.Net.Standard.Base.Bus.Control;
using Sarand.BusinessMgr.Web.Portal.Models;
using Sarand.BusinessMgr.Web.Portal.Models.Assets;
using Sarand.BusinessMgr.Web.Portal.Models.Codes;
using Sarand.Net.Standard.Base.App;
using Sarand.Net.Standard.Base.Bus;
using Sarand.Net.Standard.Base.CodeTypes;
using Sarand.Net.Standard.Base.Core;
using Sarand.Net.Standard.Base.Dbas;
using Sarand.Net.Standard.Base.Utils;
using System;
using System.Collections.Generic;
namespace Sarand.BusinessMgr.Web.Portal.Controllers.Instance
{
//=======================================================================
partial class CodeTypeController
{
//-------------------------------------------------------------------
// Definitions.
// CodeType data.
private class CodeTypeListData : AjaxResponse_Data.ItemData_Base
{
public String Code { get; set; }
public String Name { get; set; }
public long? CodeCount { get; set; }
}
private class CodeTypeItemData : CodeTypeListData
{
public bool? AutoAssignCode { get; set; }
}
private void PopulateCodeTypeResponseListData(SbmModel_Portal model, CodeTypeListData data, SaCodeType codeType)
{
data.Id = SysId.ToString(codeType.Id);
data.Code = codeType.Code;
data.Name = codeType.Name;
data.StatusCode = codeType.StatusCode;
data.StatusDesc = codeType.StatusDesc;
}
private void PopulateCodeTypeResponseItemData(SbmModel_Portal model, CodeTypeItemData data, SaCodeType codeType)
{
PopulateCodeTypeResponseListData(model, data, codeType);
if (codeType.AutoAssignCode)
data.AutoAssignCode = true;
data.Notes = codeType.Notes;
}
//-------------------------------------------------------------------
// Get CodeType List ajax request.
//-------------------------------------------------------------------
private class AjaxResponse_CodeTypeList : AjaxResponse_Data
{
// Construction.
public AjaxResponse_CodeTypeList(SbmModel_Portal model) : base(model) { }
// CodeTYpe list.
public List<CodeTypeListData> CodeTypes { get; } = new List<CodeTypeListData>();
// Reference data.
public List<CodeDesc> Statuses { get; } = new List<CodeDesc>();
public List<CodeDesc> StatusesCreate { get; } = new List<CodeDesc>();
public String StatusCodeDefault { get; set; }
}
//-------------------------------------------------------------------
[HttpPost]
[Route("Codes/CodeType/Ajax/GetList")]
[Route("Codes/CodeType/Ajax/GetCodeTypes")]
[Route("Codes/CodeType/Ajax/GetCodeTypeList")]
public IActionResult Ajax_GetCodeTypeList(Model_CodeType model)
{
String fn = GetType().Name + ".Ajax_GetCodeTypeList";
String messageTitle = Localiser["caption.code.types"];
AjaxResponse_CodeTypeList responseData = new AjaxResponse_CodeTypeList(model);
// Validate session and referrer.
if (!ValidReferer) return Forbid();
if (!ValidUserSession) return GetAjaxResult_InvalidSession(responseData);
RefreshModel(model);
// Reference data - statuses.
foreach (SaStatus status in SaCodeType.ValidStatuses)
{
responseData.Statuses.Add(SacsSystem.GetStatusCode(status), Localiser.GetStatusCaption(status));
if (status != SaStatus.Deleted)
responseData.StatusesCreate.Add(SacsSystem.GetStatusCode(status), Localiser.GetStatusCaption(status));
}
responseData.StatusCodeDefault = SacsSystem.GetStatusCode(SaCodeType.DefaultStatus);
// Item filter.
SaCodeType.Params prmCodeType = new SaCodeType.Params();
prmCodeType.Search = model.Filter.Search;
prmCodeType.StatusCat = model.Filter.GetStatusCat();
model.Paging.ItemTotal = (ulong)CodeTypeConsole.GetCodeTypeCount(prmCodeType);
model.PopulatePagingTo(prmCodeType.ResultsPaging);
// Item columns.
ColumnDfn colId = CodeTypeConsole.GetCodeTypeColumnDfn(SaCodeType.Column.Id);
ColumnDfn colCode = CodeTypeConsole.GetCodeTypeColumnDfn(SaCodeType.Column.Code);
ColumnDfn colName = CodeTypeConsole.GetCodeTypeColumnDfn(SaCodeType.Column.Name);
ColumnDfn colCodeCount = CodeTypeConsole.GetCodeTypeColumnDfn(SaCodeType.Column.Codes_Count);
ColumnDfn colStatus = CodeTypeConsole.GetCodeTypeColumnDfn(SaCodeType.Column.Status);
List<ColumnDfn> columns = new List<ColumnDfn>()
{
colId, colCode, colName,
colCodeCount, colStatus
};
prmCodeType.SortColumns.Add(colName);
prmCodeType.SortColumns.Add(colCode);
// Query items.
using (DbResultSet resultSet = CodeTypeConsole.ExecuteCodeTypeQuery(columns, prmCodeType))
{
while (resultSet != null && resultSet.HasRows && resultSet.NextRecord())
{
NamedValue.Map values = resultSet.GetRecordValues();
CodeTypeListData codeTypeData = new CodeTypeListData()
{
Id = values.GetLong(colId.DataName).ToString(),
Code = values.GetString(colCode.DataName),
Name = values.GetString(colName.DataName),
StatusCode = values.GetString(colStatus.DataName),
StatusDesc = SacsSystem.GetStatusDescForCode(values.GetString(colStatus.DataName))
};
long codeCount = values.GetLong(colCodeCount.DataName);
if (codeCount > 0)
codeTypeData.CodeCount = codeCount;
responseData.CodeTypes.Add(codeTypeData);
}
}
if (model.Paging.IsPaged)
model.Paging.ItemCount = (ushort)responseData.CodeTypes.Count;
// Complete response.
responseData.SetPaging(model);
responseData.Success = true;
return Json(responseData);
}
//-------------------------------------------------------------------
// Get CodeType ajax request.
//-------------------------------------------------------------------
private class AjaxResponse_CodeType : AjaxResponse_Data
{
// Construction.
public AjaxResponse_CodeType(SbmModel_Portal model) : base(model) { }
// CodeType details.
public CodeTypeItemData codeType { get; } = new CodeTypeItemData();
}
//-------------------------------------------------------------------
[HttpPost]
[Route("Codes/CodeType/Ajax/GetItem")]
[Route("Codes/CodeType/Ajax/GetCodeType")]
public IActionResult Ajax_GetCodeType(Model_CodeType model)
{
String fn = GetType().Name + ".Ajax_GetCodeType";
String messageTitle = Localiser["caption.asset.category"];
AjaxResponse_CodeType responseData = new AjaxResponse_CodeType(model);
// Validate session and referrer.
if (!ValidReferer) return Forbid();
if (!ValidUserSession) return GetAjaxResult_InvalidSession(responseData);
RefreshModel(model);
// Record disposable items as they are created so all can be disposed when needed.
using (DisposableCollection<SacsHierarchyItem> disposables = new DisposableCollection<SacsHierarchyItem>())
{
// Identify item.
SysId codeTypeId = model.CodeType.Id.ToSysId();
if (!SysId.IsValid(codeTypeId))
return Json(responseData.SetMessage(messageTitle, Localiser["msg.error.item.id.not.specified"]));
// Retrieve item.
SaCodeType codeType = CodeTypeConsole.GetCodeTypeById(codeTypeId);
disposables.Add(codeType);
if (codeType == null)
{
SaLogger.Warn($"{SysUtil.Fn} Failed to retrieve Code Type for Id: {codeTypeId}.");
return Json(responseData.SetMessage(messageTitle, Localiser["msg.error.item.id.not.found"]));
}
// Construct response.
PopulateCodeTypeResponseItemData(model, responseData.codeType, codeType);
responseData.Success = true;
}
return Json(responseData);
}
//-------------------------------------------------------------------
// Store CodeType ajax request.
//-------------------------------------------------------------------
[HttpPost]
[Route("Codes/CodeType/Ajax/StoreItem")]
[Route("Codes/CodeType/Ajax/StoreCodeType")]
public IActionResult Ajax_StoreCodeType(Model_CodeType model)
{
String fn = GetType().Name + ".Ajax_StoreCodeType";
String messageTitle = Localiser["caption.asset.category"];
AjaxResponse_CodeType responseData = new AjaxResponse_CodeType(model);
// Validate session and referrer.
if (!ValidReferer) return Forbid();
if (!ValidUserSession) return GetAjaxResult_InvalidSession(responseData);
RefreshModel(model);
// Validate item Id.
SysId codeTypeId = model.CodeType.Id.ToSysId();
if (StringUtil.ToBool(model.CodeType.Exists) && !SysId.IsValid(codeTypeId))
{
SaLogger.Warn($"{SysUtil.Fn} CodeType Id not supplied for existing item.");
return Json(responseData.SetMessage(messageTitle, Localiser["msg.error.system"]));
}
if (!StringUtil.ToBool(model.CodeType.Exists) && SysId.IsValid(codeTypeId))
{
SaLogger.Warn($"{SysUtil.Fn} CodeType Id supplied for new item.");
return Json(responseData.SetMessage(messageTitle, Localiser["msg.error.system"]));
}
// Validate item properties.
model.ValidateCodeTypeData(responseData);
if (responseData.HasControlErrors())
return Json(responseData);
// Record disposable items as they are created so all can be disposed when needed.
using (DisposableCollection<SacsHierarchyItem> disposables = new DisposableCollection<SacsHierarchyItem>())
{
// Retrieve existing item.
SaCodeType codeType = CodeTypeConsole.GetCodeTypeById(codeTypeId);
disposables.Add(codeType);
if (codeType == null && SysId.IsValid(codeTypeId))
{
SaLogger.Warn($"{SysUtil.Fn} Failed to retrieve Asset Category for Id: {model.CodeType.Id}.");
return Json(responseData.SetMessage(messageTitle, Localiser["msg.error.item.id.not.found"]));
}
if (codeType != null && model.CodeType.Code != null && !model.CodeType.Code.Equals(codeType.Code))
{
SaLogger.Warn($"{SysUtil.Fn} Inconsistent code returned for Code Type Id: {model.CodeType.Id}. Expected: {codeType.Code}. Found: {model.CodeType.Code}.");
return Json(responseData.SetMessage(messageTitle, Localiser["msg.error.system"]));
}
// Create new item if required.
if (codeType == null)
{
if (StringUtil.IsBlank(model.CodeType.Code))
{
// Should be picked up by model validation unless form doesn't include Code Type code.
SaLogger.Warn($"{SysUtil.Fn} Code not specified for new Code Type.");
return Json(responseData.SetMessage(messageTitle, Localiser["msg.error.system"]));
}
codeType = CodeTypeConsole.CreateCodeType(model.CodeType.Code);
}
// Populate item properties.
if (model.CodeType.Name != null)
codeType.Name = model.CodeType.Name;
if (model.CodeType.MaxCodeLength != null)
codeType.MaxCodeLen = (byte)StringUtil.ToShort(model.CodeType.MaxCodeLength);
if (model.CodeType.StatusCode != null)
codeType.Status = model.CodeType.GetStatus();
if (model.CodeType.Notes != null)
codeType.Notes = model.CodeType.Notes;
// Store item.
if (!SysId.IsValid(codeType.Id))
{
if (!CodeTypeConsole.InsertCodeType(codeType, DbUpdateInfo))
{
SaLogger.Warn($"{SysUtil.Fn} Failed to insert Asset Category: {CodeTypeConsole.ErrorStateMessage}");
return Json(responseData.SetMessage(messageTitle, Localiser["msg.error.item.insert.failed"]));
}
}
else
{
if (!CodeTypeConsole.UpdateCodeType(codeType, DbUpdateInfo))
{
SaLogger.Warn($"{SysUtil.Fn} Failed to update Asset Category [Id: {SysId.ToString(codeType.Id)}]: {CodeTypeConsole.ErrorStateMessage}");
return Json(responseData.SetMessage(messageTitle, Localiser["msg.error.item.update.failed"]));
}
}
// Construct response.
PopulateCodeTypeResponseItemData(model, responseData.codeType, codeType);
responseData.Success = true;
}
return Json(responseData);
}
//-------------------------------------------------------------------
}
//=======================================================================
}
@@ -0,0 +1,50 @@
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Logging;
using Sarand.BusinessMgr.Net.Core.Web.App;
using Sarand.BusinessMgr.Web.Portal.Models;
using Sarand.BusinessMgr.Web.Portal.Models.Assets;
using Sarand.BusinessMgr.Web.Portal.Models.Codes;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Threading.Tasks;
namespace Sarand.BusinessMgr.Web.Portal.Controllers.Instance
{
//======================================================================
public partial class CodeTypeController : SbmController_Portal
{
//-------------------------------------------------------------------
// Data members.
//-------------------------------------------------------------------
// Construction.
public CodeTypeController(SbmLocaliser localiser, ILogger<CodeTypeController> logger) :
base(localiser, logger)
{
}
//-------------------------------------------------------------------
[Route("Codes/CodeType")]
public IActionResult Index()
{
if (!ValidUserSession)
AutoLoginUser();
// Redirect to sign-in if not signed-in.
if (!ValidUserSession)
{
ClearKeepLoggedInCookie();
return RedirectToAction("", "Security");
}
// Code Type page.
Model_CodeType model = new Model_CodeType(Localiser);
return View("Views/Codes/CodeType/Index.cshtml", model);
}
//-------------------------------------------------------------------
}
//======================================================================
}
@@ -1,7 +1,7 @@
using Microsoft.AspNetCore.Mvc;
using Sarand.BusinessMgr.Net.Core.Web.App;
using Sarand.BusinessMgr.Net.Core.Web.Helpers;
using Sarand.BusinessMgr.Net.Core.Web.Portal.Models;
using Sarand.BusinessMgr.Web.Portal.Models;
using Sarand.BusinessMgr.Net.Standard.Base.App;
using Sarand.BusinessMgr.Net.Standard.Base.Bus;
using Sarand.BusinessMgr.Net.Standard.Base.Consoles;
@@ -21,13 +21,14 @@ using System.Collections.Specialized;
using System.Linq;
using System.Threading.Tasks;
using System.Web;
using Sarand.BusinessMgr.Web.Portal.Models.Commodities;
namespace Sarand.BusinessMgr.Net.Core.Web.Portal.Controllers
namespace Sarand.BusinessMgr.Web.Portal.Controllers
{
//=======================================================================
// Portal Commodity Controller - Ajax actions.
//=======================================================================
partial class CommodityController
//=======================================================================
// Portal Commodity Controller - Ajax actions.
//=======================================================================
partial class CommodityController
{
//-------------------------------------------------------------------
// Commodity data.
@@ -252,7 +253,7 @@ namespace Sarand.BusinessMgr.Net.Core.Web.Portal.Controllers
// Create new item if required.
if (commodity == null)
{
commodity = CommodityMgr.CreateCommodity(SessionData.Organisation.AffiliateId, model.GetCommodityType(), model.CommodityCode);
commodity = CommodityMgr.CreateCommodity(model.GetCommodityType(), model.CommodityCode);
if (model.CommodityCode != null)
commodity.Code = model.CommodityCode;
}
@@ -276,7 +277,7 @@ namespace Sarand.BusinessMgr.Net.Core.Web.Portal.Controllers
if (!CommodityMgr.InsertCommodity(commodity, DbUpdateInfo))
{
SaLogger.Warn(fn + " Failed to insert commodity: " + CommodityMgr.ErrorStateMessage);
return Json(responseData.SetMessage(messageTitle, Localiser["msg.error.item.insert.failed"] + "\n\n" + CommodityMgr.ErrorStateMessage));
return Json(responseData.SetMessage(messageTitle, Localiser["msg.error.item.insert.failed"]));
}
responseData.SetNotification(messageTitle, Localiser["msg.item.created"]);
}
@@ -285,7 +286,7 @@ namespace Sarand.BusinessMgr.Net.Core.Web.Portal.Controllers
if (!CommodityMgr.UpdateCommodity(commodity, DbUpdateInfo))
{
SaLogger.Warn(fn + " Failed to update commodity [Id:" + SysId.ToString(commodity.Id) + "]: " + CommodityMgr.ErrorStateMessage);
return Json(responseData.SetMessage(messageTitle, Localiser["msg.error.item.update.failed"] + "\n\n" + CommodityMgr.ErrorStateMessage));
return Json(responseData.SetMessage(messageTitle, Localiser["msg.error.item.update.failed"]));
}
responseData.SetNotification(messageTitle, Localiser["msg.item.updated"]);
}
@@ -2,7 +2,6 @@
using Microsoft.Extensions.Logging;
using Sarand.BusinessMgr.Net.Core.Web.App;
using Sarand.BusinessMgr.Net.Core.Web.Helpers;
using Sarand.BusinessMgr.Net.Core.Web.Portal.Models;
using Sarand.BusinessMgr.Net.Standard.Base.App;
using Sarand.BusinessMgr.Net.Standard.Base.Bus;
using Sarand.BusinessMgr.Net.Standard.Base.Mgr;
@@ -15,11 +14,12 @@ using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Threading.Tasks;
using Sarand.BusinessMgr.Web.Portal.Models.Commodities;
namespace Sarand.BusinessMgr.Net.Core.Web.Portal.Controllers
namespace Sarand.BusinessMgr.Web.Portal.Controllers
{
//=======================================================================
public partial class CommodityController : SbmController_Portal
//=======================================================================
public partial class CommodityController : SbmController_Portal
{
//-------------------------------------------------------------------
// Data members.
@@ -35,10 +35,15 @@ namespace Sarand.BusinessMgr.Net.Core.Web.Portal.Controllers
[HttpGet]
public IActionResult Index()
{
String fn = GetType().Name + ".Index";
if (!ValidUserSession)
return RedirectToAction("", "");
AutoLoginUser();
// Redirect to sign-in if not signed-in.
if (!ValidUserSession)
{
ClearKeepLoggedInCookie();
return RedirectToAction("", "Security");
}
// Commodity page.
Model_Commodity model = new Model_Commodity(Localiser);
@@ -1,7 +1,7 @@
using Microsoft.AspNetCore.Mvc;
using Sarand.BusinessMgr.Net.Core.Web.App;
using Sarand.BusinessMgr.Net.Core.Web.Helpers;
using Sarand.BusinessMgr.Net.Core.Web.Portal.Models;
using Sarand.BusinessMgr.Web.Portal.Models;
using Sarand.BusinessMgr.Net.Standard.Base.App;
using Sarand.BusinessMgr.Net.Standard.Base.Bus;
using Sarand.BusinessMgr.Net.Standard.Base.Consoles;
@@ -22,7 +22,7 @@ using System.Linq;
using System.Threading.Tasks;
using System.Web;
namespace Sarand.BusinessMgr.Net.Core.Web.Portal.Controllers
namespace Sarand.BusinessMgr.Web.Portal.Controllers
{
//=======================================================================
// Portal Contact Controller - Ajax actions.
@@ -348,7 +348,7 @@ namespace Sarand.BusinessMgr.Net.Core.Web.Portal.Controllers
if (!AccountMgr.InsertAffiliate(affiliate, DbUpdateInfo))
{
SaLogger.Warn(fn + " Failed to insert affiliate: " + AccountMgr.ErrorStateMessage);
return Json(responseData.SetMessage(messageTitle, Localiser["msg.error.item.insert.failed"] + "\n\n" + AccountMgr.ErrorStateMessage));
return Json(responseData.SetMessage(messageTitle, Localiser["msg.error.item.insert.failed"]));
}
responseData.SetNotification(messageTitle, Localiser["msg.item.created"]);
}
@@ -357,7 +357,7 @@ namespace Sarand.BusinessMgr.Net.Core.Web.Portal.Controllers
if (!AccountMgr.UpdateAffiliate(affiliate, DbUpdateInfo))
{
SaLogger.Warn(fn + " Failed to update affiliate [Id:" + SysId.ToString(affiliate.Id) + "]: " + AccountMgr.ErrorStateMessage);
return Json(responseData.SetMessage(messageTitle, Localiser["msg.error.item.update.failed"] + "\n\n" + AccountMgr.ErrorStateMessage));
return Json(responseData.SetMessage(messageTitle, Localiser["msg.error.item.update.failed"]));
}
responseData.SetNotification(messageTitle, Localiser["msg.item.updated"]);
}
@@ -2,7 +2,7 @@
using Microsoft.Extensions.Logging;
using Sarand.BusinessMgr.Net.Core.Web.App;
using Sarand.BusinessMgr.Net.Core.Web.Helpers;
using Sarand.BusinessMgr.Net.Core.Web.Portal.Models;
using Sarand.BusinessMgr.Web.Portal.Models;
using Sarand.BusinessMgr.Net.Standard.Base.App;
using Sarand.BusinessMgr.Net.Standard.Base.Bus;
using Sarand.BusinessMgr.Net.Standard.Base.Mgr;
@@ -16,7 +16,7 @@ using System.Diagnostics;
using System.Linq;
using System.Threading.Tasks;
namespace Sarand.BusinessMgr.Net.Core.Web.Portal.Controllers
namespace Sarand.BusinessMgr.Web.Portal.Controllers
{
//=======================================================================
public partial class ContactController : SbmController_Portal
@@ -35,10 +35,15 @@ namespace Sarand.BusinessMgr.Net.Core.Web.Portal.Controllers
[HttpGet]
public IActionResult Index()
{
String fn = GetType().Name + ".Index";
if (!ValidUserSession)
return RedirectToAction("", "");
AutoLoginUser();
// Redirect to sign-in if not signed-in.
if (!ValidUserSession)
{
ClearKeepLoggedInCookie();
return RedirectToAction("", "Security");
}
// Contact page.
Model_Contact model = new Model_Contact(Localiser);
@@ -2,7 +2,7 @@
using Microsoft.Extensions.Logging;
using Sarand.BusinessMgr.Net.Core.Web.App;
using Sarand.BusinessMgr.Net.Core.Web.Helpers;
using Sarand.BusinessMgr.Net.Core.Web.Portal.Models;
using Sarand.BusinessMgr.Web.Portal.Models;
using Sarand.BusinessMgr.Net.Standard.Base.App;
using Sarand.BusinessMgr.Net.Standard.Base.Bus;
using Sarand.BusinessMgr.Net.Standard.Base.Mgr;
@@ -16,7 +16,7 @@ using System.Diagnostics;
using System.Linq;
using System.Threading.Tasks;
namespace Sarand.BusinessMgr.Net.Core.Web.Portal.Controllers
namespace Sarand.BusinessMgr.Web.Portal.Controllers
{
//=======================================================================
public class HomeController : SbmController_Portal
@@ -35,73 +35,18 @@ namespace Sarand.BusinessMgr.Net.Core.Web.Portal.Controllers
[HttpGet]
public IActionResult Index()
{
if (!ValidUserSession)
AutoLoginUser();
// Check for 'Keep Logged In' cookie.
// NB Only allowed if there isn't a current user session.
if (!ValidUserSession && AppConfig.Security.SignIn.KeepLoggedIn.Allowed)
{
// Extract parameters and validate that they are populated.
String encryptedParams = GetRequestCookie(CookieName_KeepLoggedIn);
KeepLoggedInParams keepLoggedInParams = ExtractKeepLoggedInParams(encryptedParams);
String logPrefix = $"{SysUtil.Fn} 'Keep Logged In' cookie found but ";
String logSuffix = keepLoggedInParams.GetValidParams().Concat("", " ", ".");
if (keepLoggedInParams.IsValid())
{
// User may be defined on any one of the BusinessMgr databases - need to find out which.
// Identify session contexts with a BusMgr database that hosts the specified email address.
List<ushort> sessionContextIdxs = SbmSessionContextHelper_Web.GetSessionContextIdxsForContextUserEmail(keepLoggedInParams.DbIdentifier, keepLoggedInParams.EmailAddress);
foreach (ushort sessionContextIdx in sessionContextIdxs)
{
// Consoles and Managers must be associated with the specific context.
SbmSessionContext sessionContext = SbmSessionContextHelper_Web.GetSessionContext(sessionContextIdx);
SbmUserContext_Web userContext = new SbmUserContext_Web(sessionContext);
using (SaSecurityConsole securityConsoleForUser = App.CreateSecurityConsole(userContext))
using (SbmAccountMgr accountMgrForUser = App.System.CreateAccountMgr(userContext))
{
using (SaSecurityItem securityItemUser = securityConsoleForUser.GetUserByEmail(keepLoggedInParams.EmailAddress))
using (SbmAffiliate affiliateUser = accountMgrForUser.GetAffiliateById(keepLoggedInParams.AffiliateIdUser))
using (SbmAssociation associationOrganisation = accountMgrForUser.GetAssociationById(keepLoggedInParams.AssociationIdOrganisation))
using (SbmAffiliate affiliateOrganisation = accountMgrForUser.GetAffiliateById(associationOrganisation?.AffiliateIdOwner ?? null))
{
if (securityItemUser != null && affiliateUser != null && associationOrganisation != null && affiliateOrganisation != null)
{
if (securityItemUser.Id.Equals(keepLoggedInParams.SecurityItemId) && affiliateUser.UserId.Equals(securityItemUser.Id))
{
if (affiliateUser.IsStatusCurrent)
{
List<SysIdString> supervisedOrganisations = accountMgrForUser.GetAffiliatesSupervisedBy(affiliateUser.Id);
String errorMessage = "";
if (SignUserIn(userContext, keepLoggedInParams.DbIdentifier, securityItemUser, affiliateUser, supervisedOrganisations, associationOrganisation, affiliateOrganisation, true, ref errorMessage))
{
SaLogger.Info(logPrefix + " 'Keep Logged In' cookie found." + logSuffix);
break;
}
else SaLogger.Warn(logPrefix + "failed to sign user in: " + errorMessage + "." + logSuffix);
}
else SaLogger.Warn(logPrefix + "Affiliate is not active." + logSuffix);
}
else SaLogger.Warn(logPrefix + "Security Item and Affiliate are inconsistent. " + logSuffix);
}
else SaLogger.Warn(logPrefix + "Security Item or Affiliate not found. " + logSuffix);
}
}
if (ValidUserSession)
break;
}
}
else if (!StringUtil.IsEmpty(encryptedParams))
SaLogger.Warn($"{SysUtil.Fn} Keep Logged In cookie found but with incomplete details." + logSuffix);
}
// Redirect to sign-in if not signed-in.
if (!ValidUserSession)
{
ClearKeepLoggedInCookie();
return RedirectToAction("", "Security");
}
// Redirect to sign-in if not signed-in.
if (!ValidUserSession)
{
ClearKeepLoggedInCookie();
return RedirectToAction("", "Security");
}
// Home page.
Model_Home model = new Model_Home(Localiser);
// Home page.
Model_Home model = new Model_Home(Localiser);
model.ProhibitCache = false;
return View(model);
}
@@ -1,7 +1,8 @@
using Newtonsoft.Json;
using Microsoft.AspNetCore.Mvc.Filters;
using Newtonsoft.Json;
using Sarand.BusinessMgr.Net.Core.Web.App;
using Sarand.BusinessMgr.Net.Core.Web.Controllers;
using Sarand.BusinessMgr.Net.Core.Web.Portal.App;
using Sarand.BusinessMgr.Web.Portal.App;
using Sarand.BusinessMgr.Net.Standard.Base.App;
using Sarand.BusinessMgr.Net.Standard.Base.Bus;
using Sarand.BusinessMgr.Net.Standard.Base.Core;
@@ -16,44 +17,67 @@ using System;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Threading.Tasks;
using System.Web;
using static Sarand.BusinessMgr.Net.Standard.Base.App.Sbm.Config.Database;
using Sarand.BusinessMgr.Net.Standard.Base.Bus.Control;
using Sarand.BusinessMgr.Net.Core.Web.Helpers;
namespace Sarand.BusinessMgr.Net.Core.Web.Portal.Controllers
namespace Sarand.BusinessMgr.Web.Portal.Controllers
{
//=======================================================================
public class SbmController_Portal : SbmController_Web
{
//-------------------------------------------------------------------
// Data members.
private SbmSysParamCache sysParamCache = null;
//-------------------------------------------------------------------
// Protected construction.
protected SbmController_Portal(SbmLocaliser localiser, Microsoft.Extensions.Logging.ILogger<SbmController_Portal> logger) : base(localiser)
{
}
protected override void Dispose(bool disposing)
{
if (disposing)
{
if (sysParamCache != null) sysParamCache.Dispose();
sysParamCache = null;
}
base.Dispose(disposing);
}
//-------------------------------------------------------------------
// Session data.
//-------------------------------------------------------------------
/// <summary>Online session data</summary>
/// <summary>Portal session data</summary>
protected SbmSessionData_Portal SessionData_Portal { get { return SessionData as SbmSessionData_Portal; } }
public override ISbmUserContext_Core UserContext_Core { get { return SessionData_Portal; } }
/// <summary>
/// Session data deserialisation.
/// </summary>
protected override SbmSessionData_Web DeserializeSessionData(String sessionDataJson)
{
return JsonConvert.DeserializeObject<SbmSessionData_Portal>(sessionDataJson, SbmSessionDataJsonSerializerSettings);
}
//-------------------------------------------------------------------
// User Session.
// Properties.
// Business components.
protected SbmSysParamCache SysParamCache { get { if (sysParamCache == null) sysParamCache = new SbmSysParamCache(UserContext_Core, SessionData_Portal.User.AffiliateId); return sysParamCache; } }
//-------------------------------------------------------------------
// User Session.
/// <summary>
/// Sign the account in.
/// <para>- Record login time and clear invalid login data.</para>
/// <para>- Open user session - store details against Sesion Data.</para>
/// <para>- Set 'Keep logged in' cookie if required.</para>
/// </summary>
protected bool SignUserIn(ISbmUserContext userContext, String dbIdentifier, SaSecurityItem securityItemUser, SbmAffiliate affiliateUser, List<SysIdString> supervisedOrganisations, SbmAssociation associationOrganisation, SbmAffiliate affiliateOrganisation, bool keepLoggedIn, ref String errorMessage)
protected bool SignUserIn(ISbmUserContext_Core userContext, String dbIdentifier, SaSecurityItem securityItemUser, SbmAffiliate affiliateUser, List<SysIdString> supervisedOrganisations, SbmAssociation associationOrganisation, SbmAffiliate affiliateOrganisation, bool keepLoggedIn, ref String errorMessage)
{
// Validate Security Item and Affiliate, just in case - should have been done before arriving here!
if (securityItemUser == null)
@@ -138,7 +162,7 @@ namespace Sarand.BusinessMgr.Net.Core.Web.Portal.Controllers
protected bool RefreshSessionProfileContact(SaSecurityItem securityItemUser, SbmAffiliate affiliateUser, List<SysIdString> supervisedOrganisations, SbmAssociation associationOrganisation, SbmAffiliate affiliateOrganisation)
{
SbmSessionData_Portal sessionData = SessionData_Portal;
SysId affiliateIdUser = sessionData?.User.AffiliateId ?? null;
SysId affiliateIdUser = sessionData?.User?.AffiliateId ?? null;
if (!SysId.IsValid(affiliateIdUser)) return false;
if (!SysId.IsValid(affiliateUser?.Id ?? null)) return false;
if (!affiliateIdUser.Equals(affiliateUser.Id)) return false;
@@ -168,25 +192,112 @@ namespace Sarand.BusinessMgr.Net.Core.Web.Portal.Controllers
ClearSessionData();
}
//-------------------------------------------------------------------
// Action execution events.
//-------------------------------------------------------------------
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
base.OnActionExecuting(filterContext);
}
public override void OnActionExecuted(ActionExecutedContext filterContext)
{
// ToDo Store system params.
if (sysParamCache != null)
{
if (sysParamCache.Modified)
{
sysParamCache.Store();
SaLogger.Info($"{SysUtil.Fn} User params stored.");
}
}
base.OnActionExecuted(filterContext);
}
//-------------------------------------------------------------------
// Keep Logged In.
//-------------------------------------------------------------------
/// <summary>
/// Check for 'Keep Logged In' cookie and auto-login user if details are valid.
/// </summary>
/// <returns></returns>
protected bool AutoLoginUser()
{
// NB Only allowed if there isn't a current user session.
if (ValidUserSession) return true;
if (!AppConfig.Security.SignIn.KeepLoggedIn.Allowed) return false;
// Extract parameters and validate that they are populated.
String encryptedParams = GetRequestCookie(KeepLoggedIn.CookieName);
KeepLoggedInParams keepLoggedInParams = ExtractKeepLoggedInParams(encryptedParams);
String logPrefix = $"{SysUtil.Fn} 'Keep Logged In' cookie found but ";
String logSuffix = keepLoggedInParams.GetValidParams().Concat("", " ", ".");
if (keepLoggedInParams.IsValid())
{
// User may be defined on any one of the BusinessMgr databases - need to find out which.
// Identify session contexts with a BusMgr database that hosts the specified email address.
List<ushort> sessionContextIdxs = SbmSessionContextHelper_Web.GetSessionContextIdxsForContextUserEmail(keepLoggedInParams.DbIdentifier, keepLoggedInParams.EmailAddress);
foreach (ushort sessionContextIdx in sessionContextIdxs)
{
// Consoles and Managers must be associated with the specific context.
SbmSessionContext sessionContext = SbmSessionContextHelper_Web.GetSessionContext(sessionContextIdx);
SbmUserContext_Web userContext = new SbmUserContext_Web(sessionContext);
using (SaSecurityConsole securityConsoleForUser = App.CreateSecurityConsole(userContext))
using (SbmAccountMgr accountMgrForUser = App.System.CreateAccountMgr(userContext))
{
using (SaSecurityItem securityItemUser = securityConsoleForUser.GetUserByEmail(keepLoggedInParams.EmailAddress))
using (SbmAffiliate affiliateUser = accountMgrForUser.GetAffiliateById(keepLoggedInParams.AffiliateIdUser))
using (SbmAssociation associationOrganisation = accountMgrForUser.GetAssociationById(keepLoggedInParams.AssociationIdOrganisation))
using (SbmAffiliate affiliateOrganisation = accountMgrForUser.GetAffiliateById(associationOrganisation?.AffiliateIdOwner ?? null))
{
if (securityItemUser != null && affiliateUser != null && associationOrganisation != null && affiliateOrganisation != null)
{
if (securityItemUser.Id.Equals(keepLoggedInParams.SecurityItemId) && affiliateUser.UserId.Equals(securityItemUser.Id))
{
if (affiliateUser.IsStatusCurrent)
{
List<SysIdString> supervisedOrganisations = accountMgrForUser.GetAffiliatesSupervisedBy(affiliateUser.Id);
String errorMessage = "";
if (SignUserIn(userContext, keepLoggedInParams.DbIdentifier, securityItemUser, affiliateUser, supervisedOrganisations, associationOrganisation, affiliateOrganisation, true, ref errorMessage))
{
SaLogger.Info(logPrefix + " 'Keep Logged In' cookie found." + logSuffix);
return true;
}
else SaLogger.Warn(logPrefix + "failed to sign user in: " + errorMessage + "." + logSuffix);
}
else SaLogger.Warn(logPrefix + "Affiliate is not active." + logSuffix);
}
else SaLogger.Warn(logPrefix + "Security Item and Affiliate are inconsistent. " + logSuffix);
}
else SaLogger.Warn(logPrefix + "Security Item or Affiliate not found. " + logSuffix);
}
}
if (ValidUserSession)
break;
}
}
else if (!StringUtil.IsEmpty(encryptedParams))
SaLogger.Warn($"{SysUtil.Fn} Keep Logged In cookie found but with incomplete details." + logSuffix);
ClearKeepLoggedInCookie();
return false;
}
//-------------------------------------------------------------------
/// <summary>Keep-Logged-In cookie.</summary>
protected void SetKeepLoggedInCookie()
{
String keepLoggedInCookieValue = GenerateKeepLoggedInCookieValue();
int cookieExpiresDays = Math.Max(Sbm.Config.Security.SignIn.KeepLoggedIn.CookieExpiresDays_Min, Math.Min(Sbm.Config.Security.SignIn.KeepLoggedIn.CookieExpiresDays_Max, AppConfig.Security.SignIn.KeepLoggedIn.CookieExpiresDays));
SetResponseCookie(CookieName_KeepLoggedIn, keepLoggedInCookieValue, DateTime.Now.AddDays(cookieExpiresDays), true);
SaLogger.Info($"{SysUtil.Fn} 'Keep Logged In' cookie set. Value: {keepLoggedInCookieValue}");
SetResponseCookie(KeepLoggedIn.CookieName, keepLoggedInCookieValue, DateTime.Now.AddDays(cookieExpiresDays), true);
SaLogger.Info($"{SysUtil.Fn} 'Keep Logged In' cookie set. Name: {KeepLoggedIn.CookieName}. Value: {keepLoggedInCookieValue}");
}
/// <summary>Set the 'Keep Logged In' response cookie to expired with no data.</summary>
public void ClearKeepLoggedInCookie()
{
if (StringUtil.IsEmpty(GetRequestCookie(CookieName_KeepLoggedIn))) return;
if (StringUtil.IsEmpty(GetRequestCookie(KeepLoggedIn.CookieName))) return;
// Clear cookie by setting expiry date in the past.
SetResponseCookie(CookieName_KeepLoggedIn, "", DateTime.Today.AddDays(-7), true);
SaLogger.Info($"{SysUtil.Fn} 'Keep Logged In' cookie cleared.");
SetResponseCookie(KeepLoggedIn.CookieName, "", DateTime.Today.AddDays(-7), true);
SaLogger.Info($"{SysUtil.Fn} 'Keep Logged In' cookie cleared. Name: {KeepLoggedIn.CookieName}.");
}
//-------------------------------------------------------------------
@@ -195,11 +306,14 @@ namespace Sarand.BusinessMgr.Net.Core.Web.Portal.Controllers
// to ensure that cookie value can be decrypted even if application restarts in the meantime.
// - Generating IV from same key results in different value each time.
// - IV Must be regenerated if key is changed: uncomment 'Generated' variable and copy resulting value to IV variable.
// NB - 'Keep Logged In' is termed 'Remember Me' in the Online application.
public static String CookieName_KeepLoggedIn = "SBMRM";
public static String Base64Key_KeepLoggedIn = SacsEncryptionHelper.EncodePlainKey("<=SaBusMgr^P0rt4L{KeepLoggedIn}~", SacsEncryptionHelper.KeyLength);
//public static String Base64IV_KeepLoggedIn_Generated = SacsEncryptionHelper.InitSymmetricEncryptionIV(Base64Key_KeepLoggedIn);
public static String Base64IV_KeepLoggedIn = "UU2UJ2p2tXBihMyUEi9nJg==";
// NB - 'Keep Logged In' is termed 'Remember Me' in the Portal application.
public static class KeepLoggedIn
{
public static String CookieName = "sbprm";
public static String Base64Key = SacsEncryptionHelper.EncodePlainKey("<=SaBusMgr^P0rt4L{KeepLoggedIn}~", SacsEncryptionHelper.KeyLength);
//private static String KeepLoggedIn.Base64IV_Generated = SacsEncryptionHelper.InitSymmetricEncryptionIV(KeepLoggedIn.Base64Key);
public static String Base64IV = "m47AMcMgACp3aoQVc0Y92A==";
}
//-------------------------------------------------------------------
// Keep-logged-in cookie value.
@@ -216,7 +330,7 @@ namespace Sarand.BusinessMgr.Net.Core.Web.Portal.Controllers
keepLoggedInParams.Add(ParamName_KeepLoggedIn_SecurityItemId, SysId.ToString(SessionData_Portal.User.SecurityItemId));
keepLoggedInParams.Add(ParamName_KeepLoggedIn_AffiliateIdUser, SysId.ToString(SessionData_Portal.User.AffiliateId));
keepLoggedInParams.Add(ParamName_KeepLoggedIn_AssociationIdOrganisation, SysId.ToString(SessionData_Portal.Organisation.AssociationId));
return EncryptParameters(keepLoggedInParams, Base64IV_KeepLoggedIn, Base64Key_KeepLoggedIn);
return EncryptParameters(keepLoggedInParams, KeepLoggedIn.Base64IV, KeepLoggedIn.Base64Key);
}
//-------------------------------------------------------------------
@@ -248,7 +362,7 @@ namespace Sarand.BusinessMgr.Net.Core.Web.Portal.Controllers
{
return
!StringUtil.IsBlank(DbIdentifier) &&
StringUtil.IsBlank(EmailAddress) &&
!StringUtil.IsBlank(EmailAddress) &&
SysId.IsValid(SecurityItemId) &&
SysId.IsValid(AffiliateIdUser) &&
SysId.IsValid(AssociationIdOrganisation);
@@ -259,7 +373,7 @@ namespace Sarand.BusinessMgr.Net.Core.Web.Portal.Controllers
KeepLoggedInParams keepLoggedInParams = new KeepLoggedInParams();
if (!StringUtil.IsEmpty(encryptedParams))
{
NameValueCollection linkParams = DecryptParameters(encryptedParams, Base64IV_KeepLoggedIn, Base64Key_KeepLoggedIn);
NameValueCollection linkParams = DecryptParameters(encryptedParams, KeepLoggedIn.Base64IV, KeepLoggedIn.Base64Key);
keepLoggedInParams.DbIdentifier = linkParams.Get(ParamName_KeepLoggedIn_DbIdentifier);
keepLoggedInParams.EmailAddress = linkParams.Get(ParamName_KeepLoggedIn_EmailAddress);
keepLoggedInParams.SecurityItemId = linkParams.Get(ParamName_KeepLoggedIn_SecurityItemId).ToSysId();
@@ -274,18 +388,18 @@ namespace Sarand.BusinessMgr.Net.Core.Web.Portal.Controllers
// Parameter encryption, eg. for images.
//-------------------------------------------------------------------
// Encryption key is the same for all users but a different IV value is generated for each session so that the links/parameters cannot be shared.
public static String Base64Key_OnlineApp { get; } = SacsEncryptionHelper.EncodePlainKey("{SaBusMgr=Portal-App#Encryption}", SacsEncryptionHelper.KeyLength);
protected String EncryptOnlineParams(NameValueCollection parameters)
public static String Base64Key_PortalApp { get; } = SacsEncryptionHelper.EncodePlainKey("{SaBusMgr=Portal-App#Encryption}", SacsEncryptionHelper.KeyLength);
protected String EncryptPortalParams(NameValueCollection parameters)
{
return HttpUtility.UrlEncode(EncryptParameters(parameters, SessionData_Portal.Base64IV_PortalApp, Base64Key_OnlineApp));
return HttpUtility.UrlEncode(EncryptParameters(parameters, SessionData_Portal.Base64IV_PortalApp, Base64Key_PortalApp));
}
public static String EncryptOnlineParams(SbmController_Portal controller, NameValueCollection parameters)
public static String EncryptPortalParams(SbmController_Portal controller, NameValueCollection parameters)
{
return controller.EncryptOnlineParams(parameters);
return controller.EncryptPortalParams(parameters);
}
protected NameValueCollection ExtractOnlineParams(String encryptedParams)
protected NameValueCollection ExtractPortalParams(String encryptedParams)
{
return DecryptParameters(encryptedParams, SessionData_Portal.Base64IV_PortalApp, Base64Key_OnlineApp);
return DecryptParameters(encryptedParams, SessionData_Portal.Base64IV_PortalApp, Base64Key_PortalApp);
}
//-------------------------------------------------------------------
@@ -2,13 +2,15 @@
using NPOI.SS.Formula.Functions;
using Sarand.BusinessMgr.Net.Core.Web.App;
using Sarand.BusinessMgr.Net.Core.Web.Helpers;
using Sarand.BusinessMgr.Net.Core.Web.Portal.Models;
using Sarand.BusinessMgr.Web.Portal.App;
using Sarand.BusinessMgr.Web.Portal.Models;
using Sarand.BusinessMgr.Net.Standard.Base.App;
using Sarand.BusinessMgr.Net.Standard.Base.Bus;
using Sarand.BusinessMgr.Net.Standard.Base.Consoles;
using Sarand.BusinessMgr.Net.Standard.Base.Core;
using Sarand.BusinessMgr.Net.Standard.Base.Helpers;
using Sarand.BusinessMgr.Net.Standard.Base.Mgr;
using Sarand.BusinessMgr.Net.Standard.Dbas.App;
using Sarand.Net.Standard.Base.App;
using Sarand.Net.Standard.Base.Bus;
using Sarand.Net.Standard.Base.Core;
@@ -23,8 +25,12 @@ using System.Collections.Specialized;
using System.Linq;
using System.Threading.Tasks;
using System.Web;
using DocumentFormat.OpenXml.EMMA;
using static Sarand.Net.Standard.Base.Bus.SaErrorStateItem;
using Sarand.BusinessMgr.Net.Standard.Base.Bus.Control;
using Sarand.BusinessMgr.Web.Portal.Helpers;
namespace Sarand.BusinessMgr.Net.Core.Web.Portal.Controllers
namespace Sarand.BusinessMgr.Web.Portal.Controllers
{
//=======================================================================
// Portal Security Controller - Ajax actions.
@@ -54,95 +60,129 @@ namespace Sarand.BusinessMgr.Net.Core.Web.Portal.Controllers
model.ValidateSignInData(responseData);
if (responseData.HasControlErrors())
return Json(responseData);
using (DisposableCollection<IDisposable> disposableItems = new DisposableCollection<IDisposable>())
using (DisposableCollection<IDisposable> disposables = new DisposableCollection<IDisposable>())
{
// Identify session contexts with a BusMgr database that hosts the specified email address.
List<ushort> sessionContextIdxs = SbmSessionContextHelper_Web.GetSessionContextIdxsForContextUserEmail(model.DbIdentifier, model.EmailAddress);
if (sessionContextIdxs.IsEmpty())
return Json(responseData.SetControlError(nameof(model.EmailAddress), model.Localiser["security.msg.signin.invalid"]));
foreach (ushort sessionContextIdx in sessionContextIdxs)
if (!(SessionData_Portal?.ValidUser ?? false))
{
SbmSessionContext sessionContext = SbmSessionContextHelper_Web.GetSessionContext(sessionContextIdx);
SbmUserContext_Web userContext = new SbmUserContext_Web(sessionContext);
ISacsDatabase busMgrDatabase = App.GetBusMgrDatabase(sessionContext);
if (busMgrDatabase == null) continue;
// Account manager.
SaSecurityConsole securityConsoleForUser = App.CreateSecurityConsole(userContext);
disposableItems.Add(securityConsoleForUser);
SbmAccountMgr accountMgrForUser = SbmGlobals_Web.System.CreateAccountMgr(userContext);
disposableItems.Add(accountMgrForUser);
// User's security item.
SaSecurityItem securityItemUser = GetSecurityItemForSignin(securityConsoleForUser, model);
disposableItems.Add(securityItemUser);
if (securityItemUser == null)
{
SaLogger.Warn($"{SysUtil.Fn} Failed to retrieve Security Item. Database: {busMgrDatabase.DatabaseName}. Email: {model.EmailAddress}.");
continue;
}
if (!securityItemUser.PasswordMatches(model.Password))
{
SaLogger.Warn($"{SysUtil.Fn} Supplied password doesn't match Security Item. Database: {busMgrDatabase.DatabaseName}. Email: {model.EmailAddress}.");
continue;
}
// User's affiliate.
SbmAffiliate affiliateUser = GetAffiliateForSignin(accountMgrForUser, model, securityItemUser);
disposableItems.Add(affiliateUser);
if (affiliateUser == null)
{
SaLogger.Warn($"{SysUtil.Fn} Failed to retrieve Affiliate for signin. Database: {busMgrDatabase.DatabaseName}. UserId: {SysId.ToString(securityItemUser.Id)}.");
continue;
}
// User's supervised organisations.
List<SysIdString> supervisedOrganisations = accountMgrForUser.GetAffiliatesSupervisedBy(affiliateUser.Id);
if (supervisedOrganisations.IsEmpty())
{
SaLogger.Warn($"{SysUtil.Fn} User has no supervised organisations. Database: {busMgrDatabase.DatabaseName}. Email: {model.EmailAddress}.");
continue;
}
userContext.RefreshUser(securityItemUser, affiliateUser, supervisedOrganisations);
// User's organisation association.
SbmAssociation associationOrganisation = GetOrganisationAssociationForSignIn(accountMgrForUser, model, userContext);
disposableItems.Add(associationOrganisation);
if (associationOrganisation == null)
{
SaLogger.Warn($"{SysUtil.Fn} Failed to identify Organisation Association. Database: {busMgrDatabase.DatabaseName}. Email: {model.EmailAddress}.");
continue;
}
// User's organisation affiliate.
SbmAffiliate affiliateOrganisation = accountMgrForUser.GetAffiliateById(associationOrganisation?.AffiliateIdOwner ?? null);
disposableItems.Add(affiliateOrganisation);
if (affiliateOrganisation == null)
{
SaLogger.Warn($"{SysUtil.Fn} Failed to retrieve Affiliate for organisation. Database: {busMgrDatabase.DatabaseName}. Id: " + SysId.ToString(associationOrganisation?.Id ?? null));
continue;
}
// Initialise user session.
// Instance Repository configured on Control database.
SbmSessionContext sessionContext = SbmSessionContextHelper_Portal.GetControlSessionContext(model.DbIdentifier);
String errorMessage = "";
if (SignUserIn(userContext, model.DbIdentifier, securityItemUser, affiliateUser, supervisedOrganisations, associationOrganisation, affiliateOrganisation, model.KeepMeSignedIn, ref errorMessage))
break;
else return Json(responseData.SetControlError(nameof(model.EmailAddress), errorMessage));
if (!OpenUserSession(model, sessionContext, ref errorMessage))
{
if (!StringUtil.IsBlank(errorMessage))
return Json(responseData.SetControlError(nameof(model.EmailAddress), errorMessage));
}
}
if (!(SessionData_Portal?.ValidUser ?? false))
{
// AppSettings configured session contexts with a BusMgr database that hosts the specified email address.
List<ushort> sessionContextIdxs = SbmSessionContextHelper_Web.GetSessionContextIdxsForContextUserEmail(model.DbIdentifier, model.EmailAddress);
if (sessionContextIdxs.IsEmpty())
return Json(responseData.SetControlError(nameof(model.EmailAddress), model.Localiser["security.msg.signin.invalid"]));
foreach (ushort sessionContextIdx in sessionContextIdxs)
{
SbmSessionContext sessionContext = SbmSessionContextHelper_Web.GetSessionContext(sessionContextIdx);
String errorMessage = "";
if (OpenUserSession(model, sessionContext, ref errorMessage)) break;
if (!StringUtil.IsBlank(errorMessage))
return Json(responseData.SetControlError(nameof(model.EmailAddress), errorMessage));
}
}
}
if (!(SessionData_Portal?.ValidUserSession ?? false))
if (!(SessionData_Portal?.ValidUser ?? false))
{
SaLogger.Warn($"{SysUtil.Fn} Failed to identify Suitable database. Email: {model.EmailAddress}.");
SaLogger.Warn($"{SysUtil.Fn} Failed to identify Suitable database. Context: {model.DbIdentifier}. Email: {model.EmailAddress}.");
return Json(responseData.SetControlError(nameof(model.EmailAddress), model.Localiser["security.msg.signin.invalid"]));
}
// Construct response.
responseData.Success = SessionData_Portal?.ValidUserSession ?? false;
responseData.Success = SessionData_Portal?.ValidUser ?? false;
return Json(responseData);
}
private bool OpenUserSession(Model_Security model, SbmSessionContext sessionContext, ref String errorMessage)
{
if (sessionContext == null) return false;
SbmUserContext_Web userContext = new SbmUserContext_Web(sessionContext);
ISacsDatabase busMgrDatabase = App.GetBusMgrDatabase(sessionContext);
if (busMgrDatabase == null) return false;
using (DisposableCollection<IDisposable> disposables = new DisposableCollection<IDisposable>())
{
// Account manager.
SaSecurityConsole securityConsoleForUser = App.CreateSecurityConsole(userContext);
disposables.Add(securityConsoleForUser);
SbmAccountMgr accountMgrForUser = SbmGlobals_Web.System.CreateAccountMgr(userContext);
disposables.Add(accountMgrForUser);
// User's security item.
SaSecurityItem securityItemUser = GetSecurityItemForSignin(securityConsoleForUser, model);
disposables.Add(securityItemUser);
if (securityItemUser == null)
{
SaLogger.Warn($"{SysUtil.Fn} Failed to retrieve Security Item. Database: {busMgrDatabase.DatabaseName}. Email: {model.EmailAddress}.");
return false;
}
if (!securityItemUser.PasswordMatches(model.Password))
{
SaLogger.Warn($"{SysUtil.Fn} Supplied password doesn't match Security Item. Database: {busMgrDatabase.DatabaseName}. Email: {model.EmailAddress}.");
return false;
}
// User's affiliate.
SbmAffiliate affiliateUser = GetAffiliateForSignin(accountMgrForUser, model, securityItemUser);
disposables.Add(affiliateUser);
if (affiliateUser == null)
{
SaLogger.Warn($"{SysUtil.Fn} Failed to retrieve Affiliate for signin. Database: {busMgrDatabase.DatabaseName}. UserId: {SysId.ToString(securityItemUser.Id)}.");
return false;
}
// User's supervised organisations.
List<SysIdString> supervisedOrganisations = accountMgrForUser.GetAffiliatesSupervisedBy(affiliateUser.Id);
if (supervisedOrganisations.IsEmpty())
{
SaLogger.Warn($"{SysUtil.Fn} User has no supervised organisations. Database: {busMgrDatabase.DatabaseName}. Email: {model.EmailAddress}.");
return false;
}
userContext.RefreshUser(securityItemUser, affiliateUser, supervisedOrganisations);
// User's organisation association.
SbmAssociation associationOrganisation = GetOrganisationAssociationForSignIn(accountMgrForUser, model, userContext);
disposables.Add(associationOrganisation);
if (associationOrganisation == null)
{
SaLogger.Warn($"{SysUtil.Fn} Failed to identify Organisation Association. Database: {busMgrDatabase.DatabaseName}. Email: {model.EmailAddress}.");
return false;
}
// User's organisation affiliate.
SbmAffiliate affiliateOrganisation = accountMgrForUser.GetAffiliateById(associationOrganisation?.AffiliateIdOwner ?? null);
disposables.Add(affiliateOrganisation);
if (affiliateOrganisation == null)
{
SaLogger.Warn($"{SysUtil.Fn} Failed to retrieve Affiliate for organisation. Database: {busMgrDatabase.DatabaseName}. Id: " + SysId.ToString(associationOrganisation?.Id ?? null));
return false;
}
// Initialise user session.
if (SignUserIn(userContext, model.DbIdentifier, securityItemUser, affiliateUser, supervisedOrganisations, associationOrganisation, affiliateOrganisation, model.KeepMeSignedIn, ref errorMessage))
return true;
}
return false;
}
//-------------------------------------------------------------------
private SaSecurityItem GetSecurityItemForSignin(SaSecurityConsole securityConsoleForUser, Model_Security model)
{
@@ -203,10 +243,10 @@ namespace Sarand.BusinessMgr.Net.Core.Web.Portal.Controllers
private SbmAssociation GetOrganisationAssociationForSignIn(SbmAccountMgr accountMgrForUser, Model_Security model, SbmUserContext userContext)
{
// Previously selected organisation is stored in user params (SysParam table).
SbmSysParamCache sysParamCache = new SbmSysParamCache(userContext);
SbmSysParamCache sysParamCache = new SbmSysParamCache(userContext, userContext.UserAffiliateId);
SysId affiliateIdOrganisation = StringUtil.ToSysId(sysParamCache.GetUserParamValue(SbmSysParamCache.ParamName_SelectedCompany));
SbmAssociation associationOrganisation = null;
foreach (SysIdString supervisedOrganisation in userContext.User.SupervisedOrganisations)
foreach (SysIdString supervisedOrganisation in userContext.SupervisedOrganisations)
{
SbmAssociation associationOrganisationCandidate = accountMgrForUser.GetAssociationByOwnerTypeTarget(supervisedOrganisation.Id, Sbm.AssociationType.Internal, supervisedOrganisation.Id);
if (associationOrganisationCandidate == null)
@@ -253,16 +293,14 @@ namespace Sarand.BusinessMgr.Net.Core.Web.Portal.Controllers
AjaxResponse_KeepLoggedIn responseData = new AjaxResponse_KeepLoggedIn();
// Validate session and referrer.
if (!ValidReferer)
return Forbid();
if (!ValidUserSession)
return Json(responseData.SetGoHome());
if (!ValidReferer) return Forbid();
if (!ValidUserSession) return Json(responseData.SetGoHome());
if (!AppConfig.Security.SignIn.KeepLoggedIn.Allowed)
return Json(responseData);
// Resend the cookie to extend the expiry date if it is currently populated.
if (!StringUtil.IsEmpty(GetRequestCookie(CookieName_KeepLoggedIn)))
if (!StringUtil.IsEmpty(GetRequestCookie(KeepLoggedIn.CookieName)))
SetKeepLoggedInCookie();
// Construct response.
@@ -302,7 +340,7 @@ namespace Sarand.BusinessMgr.Net.Core.Web.Portal.Controllers
}
// Clear user session.
String emailAddress = SessionData.User.EmailAddress;
String emailAddress = SessionData.UserEmailAddress;
CloseUserSession();
SaLogger.Info($"{SysUtil.Fn} User session closed for {emailAddress}.", true);
@@ -552,10 +590,10 @@ namespace Sarand.BusinessMgr.Net.Core.Web.Portal.Controllers
responseData.SuccessMsg = Localiser["security.msg.request.password.email.sent"];
return Json(responseData);
}
private void PopulateRequestPasswordEmailBody(SbmEmailConsole emailConsole, ISbmUserContext userContext, SaEmailData emailData, SaSecurityItem securityItemUser, SbmAffiliate affiliateUser)
private void PopulateRequestPasswordEmailBody(SbmEmailConsole emailConsole, ISbmUserContext_Core userContext, SaEmailData emailData, SaSecurityItem securityItemUser, SbmAffiliate affiliateUser)
{
// Will need to handle culture (language/country) as and when introduced.
emailData.MessageBody = SbmResourceHelper.GetTextResource(SbmResourceHelper.TextResource.Email_RequestPassword_Html);
emailData.MessageBody = SbmResourceHelper.GetResourceText(SbmResourceHelper.TextResource.Email_RequestPassword_Html);
emailData.HtmlBody = true;
// Replace dynamic tags.
@@ -575,11 +613,8 @@ namespace Sarand.BusinessMgr.Net.Core.Web.Portal.Controllers
private static String ParamName_AffiliateId { get; } = "afi";
private static String ParamName_ExpiresAt { get; } = "exp";
private static String ParamName_Token { get; } = "tok";
// NB IV value is generated from the key and fixed so that it doesn't change when the application restarts.
// IV Must be regenerated if key is changed.
private static String Base64Key_RequestPassword { get; } = SacsEncryptionHelper.EncodePlainKey("[-SaBusMgr+<Request=Password>!^]", SacsEncryptionHelper.KeyLength);
//private static String Base64IV_RequestPassword_Generated { get; } = SacsEncryptionHelper.InitSymmetricEncryptionIV(Base64Key_RequestPassword);
private static String Base64IV_RequestPassword { get; } = "iALRIxzrZIfrB4boNFRXfA==";
// Portal Request Password encryption parameters (key & IV) are defined in the base Controller
// so that they can be shared with the Console application.
private String GenerateRequestPasswordLink(String dbIdentifier, SysId securityItemId, SysId affiliateId, String emailAddress, String requestPasswordToken)
{
NameValueCollection parameters = new NameValueCollection();
@@ -591,7 +626,7 @@ namespace Sarand.BusinessMgr.Net.Core.Web.Portal.Controllers
parameters.Add(ParamName_Token, requestPasswordToken);
String link =
GetRedirectLocation(RedirectLocation.WebAppBase) + "Security/ChangePassword?" +
ParamName_RequestPasswordEncrypted + "=" + HttpUtility.UrlEncode(EncryptParameters(parameters, Base64IV_RequestPassword, Base64Key_RequestPassword));
ParamName_RequestPasswordEncrypted + "=" + HttpUtility.UrlEncode(EncryptParameters(parameters, Base64IV_RequestPassword_Portal, Base64Key_RequestPassword_Portal));
return link;
}
private int GetRequestPasswordEmailLinkExpiresMinutes()
@@ -654,7 +689,7 @@ namespace Sarand.BusinessMgr.Net.Core.Web.Portal.Controllers
RequestPasswordParams requestPasswordParams = new RequestPasswordParams();
if (!StringUtil.IsEmpty(encryptedParams))
{
NameValueCollection linkParams = DecryptParameters(encryptedParams, Base64IV_RequestPassword, Base64Key_RequestPassword);
NameValueCollection linkParams = DecryptParameters(encryptedParams, Base64IV_RequestPassword_Portal, Base64Key_RequestPassword_Portal);
requestPasswordParams.DbIdentifier = linkParams.Get(ParamName_DbIdentifier);
requestPasswordParams.EmailAddress = linkParams.Get(ParamName_EmailAddress);
requestPasswordParams.SecurityItemId = new SysId(StringUtil.ToInt(linkParams.Get(ParamName_SecurityItemId)));
@@ -3,7 +3,7 @@ using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Logging;
using Sarand.BusinessMgr.Net.Core.Web.App;
using Sarand.BusinessMgr.Net.Core.Web.Helpers;
using Sarand.BusinessMgr.Net.Core.Web.Portal.Models;
using Sarand.BusinessMgr.Web.Portal.Models;
using Sarand.BusinessMgr.Net.Standard.Base.App;
using Sarand.BusinessMgr.Net.Standard.Base.Bus;
using Sarand.BusinessMgr.Net.Standard.Base.Mgr;
@@ -17,7 +17,7 @@ using System.Diagnostics;
using System.Linq;
using System.Threading.Tasks;
namespace Sarand.BusinessMgr.Net.Core.Web.Portal.Controllers
namespace Sarand.BusinessMgr.Web.Portal.Controllers
{
//=======================================================================
// Online Security Controller.
@@ -0,0 +1,26 @@
using Sarand.BusinessMgr.Net.Core.Web.App;
using Sarand.BusinessMgr.Net.Standard.Base.App;
using Sarand.BusinessMgr.Web.Portal.App;
using System;
using System.Collections.Generic;
namespace Sarand.BusinessMgr.Web.Portal.Helpers
{
//=======================================================================
// Portal Session Context utility functions.
//=======================================================================
public class SbmSessionContextHelper_Portal
{
//-------------------------------------------------------------------
// Control Session Contexts.
//-------------------------------------------------------------------
public static SbmSessionContext GetControlSessionContext(String dbIdentifier)
{
SbmApplication_Portal app = SbmApplication_Portal.Instance;
return app?.GetControlSessionContext(dbIdentifier);
}
//-------------------------------------------------------------------
}
//=======================================================================
}
@@ -0,0 +1,122 @@
using DocumentFormat.OpenXml.Spreadsheet;
using Sarand.BusinessMgr.Net.Core.Web.App;
using Sarand.BusinessMgr.Net.Core.Web.Controllers;
using Sarand.BusinessMgr.Net.Standard.Base.App;
using Sarand.BusinessMgr.Net.Standard.Base.Bus;
using Sarand.BusinessMgr.Net.Standard.Base.Bus.Control;
using Sarand.BusinessMgr.Web.Portal.Models;
using Sarand.Net.Standard.Base.App;
using Sarand.Net.Standard.Base.Bus;
using Sarand.Net.Standard.Base.Core;
using Sarand.Net.Standard.Base.Helpers;
using Sarand.Net.Standard.Base.Utils;
using System;
namespace Sarand.BusinessMgr.Web.Portal.Models.Assets
{
//=======================================================================
public class Model_Asset : SbmModel_Portal
{
//-------------------------------------------------------------------
// Construction.
public Model_Asset(SbmLocaliser localiser) :
base(localiser)
{
}
//-------------------------------------------------------------------
// Filter properties.
public class FilterData
{
public String Search { get; set; }
public String StatusCat { get; set; }
public SbmAsset.StatusCat GetStatusCat()
{
return EnumUtil.ParseEnum(StatusCat, SbmAsset.StatusCat.NotDeleted);
}
}
public FilterData Filter { get; } = new FilterData();
//-------------------------------------------------------------------
// Item properties.
public class AssetData
{
public String Exists { get; set; }
public String Id { get; set; }
public String CategoryId { get; set; }
public String SerialNumber{ get; set; }
public String DescBrief { get; set; }
public String Notes { get; set; }
public String StatusCode { get; set; }
public SaStatus GetStatus() { return SacsSystem.GetStatusForCode(StatusCode); }
}
public AssetData Asset { get; } = new AssetData();
//-------------------------------------------------------------------
// Validation.
//-------------------------------------------------------------------
// Asset.
public bool ValidateAssetData(SbmController_Base.AjaxResponse_Data responseData)
{
// Record disposable items as they are created so all can be disposed when needed.
using (DisposableCollection<SacsHierarchyItem> disposables = new DisposableCollection<SacsHierarchyItem>())
{
// Asset Category.
if (Asset.CategoryId != null)
{
String controlName = "AssetCategory";
if (StringUtil.IsBlank(Asset.CategoryId))
responseData.SetControlError(controlName, Localiser["msg.validation.mandatory.value"]);
else if (!AssetMgr.AssetCategoryExistsById(Asset.CategoryId.ToSysId()))
responseData.SetControlError(controlName, Localiser["msg.validation.invalid.value"]);
}
// Serial Number.
if (Asset.SerialNumber != null)
{
String controlName = "AssetSerialNumber";
Asset.SerialNumber = Asset.SerialNumber.ToUpper();
responseData.SetControlValue(controlName, Asset.SerialNumber);
if (StringUtil.IsBlank(Asset.SerialNumber))
responseData.SetControlError(controlName, Localiser["msg.validation.mandatory.value"]);
else if (StringUtil.GetLength(Asset.SerialNumber) > GetControlMaxLength(SbmDbDfn.ColumnIdx.SaAsset_SerialNumber))
responseData.SetControlError(controlName, Localiser["msg.validation.maximum.length", GetControlMaxLength(SbmDbDfn.ColumnIdx.SaAsset_SerialNumber)]);
}
// Description.
if (Asset.DescBrief != null)
{
String controlName = "AssetDescription";
responseData.SetControlValue(controlName, Asset.DescBrief);
if (StringUtil.GetLength(Asset.DescBrief) > GetControlMaxLength(SbmDbDfn.ColumnIdx.SaAsset_DescBrief))
responseData.SetControlError(controlName, Localiser["msg.validation.maximum.length", GetControlMaxLength(SbmDbDfn.ColumnIdx.SaAsset_DescBrief)]);
}
// Notes.
if (Asset.Notes!= null)
{
String controlName = "AssetNotes";
responseData.SetControlValue(controlName, Asset.Notes);
if (StringUtil.GetLength(Asset.Notes) > GetControlMaxLength(SbmDbDfn.ColumnIdx.SaAsset_Notes))
responseData.SetControlError(controlName, Localiser["msg.validation.maximum.length", GetControlMaxLength(SbmDbDfn.ColumnIdx.SaAsset_Notes)]);
}
// Asset status.
if (Asset.StatusCode != null)
{
String controlName = "AssetStatus";
if (StringUtil.IsBlank(Asset.StatusCode))
responseData.SetControlError(controlName, Localiser["msg.validation.mandatory.value"]);
else if (!SbmAsset.ValidStatuses.Contains(Asset.GetStatus()))
responseData.SetControlError(controlName, Localiser["msg.validation.invalid.value"]);
}
}
return !responseData.HasControlErrors();
}
//-------------------------------------------------------------------
}
//=======================================================================
}
@@ -0,0 +1,127 @@
using DocumentFormat.OpenXml.Spreadsheet;
using Sarand.BusinessMgr.Net.Core.Web.App;
using Sarand.BusinessMgr.Net.Core.Web.Controllers;
using Sarand.BusinessMgr.Net.Standard.Base.App;
using Sarand.BusinessMgr.Net.Standard.Base.Bus;
using Sarand.BusinessMgr.Net.Standard.Base.Bus.Control;
using Sarand.BusinessMgr.Web.Portal.Models;
using Sarand.Net.Standard.Base.App;
using Sarand.Net.Standard.Base.Bus;
using Sarand.Net.Standard.Base.Core;
using Sarand.Net.Standard.Base.Helpers;
using Sarand.Net.Standard.Base.Utils;
using System;
namespace Sarand.BusinessMgr.Web.Portal.Models.Assets
{
//=======================================================================
public class Model_AssetCategory : SbmModel_Portal
{
//-------------------------------------------------------------------
// Construction.
public Model_AssetCategory(SbmLocaliser localiser) :
base(localiser)
{
}
//-------------------------------------------------------------------
// Filter properties.
public class FilterData
{
public String Search { get; set; }
public String StatusCat { get; set; }
public SbmAssetCategory.StatusCat GetStatusCat()
{
return EnumUtil.ParseEnum(StatusCat, SbmAssetCategory.StatusCat.NotDeleted);
}
}
public FilterData Filter { get; } = new FilterData();
//-------------------------------------------------------------------
// Item properties.
public class AssetCategoryData
{
public String Exists { get; set; }
public String Id { get; set; }
public String TypeCode { get; set; }
public Sbm.AssetType AssetType { get; set; }
public String Code { get; set; }
public String DescBrief { get; set; }
public String Notes { get; set; }
public String StatusCode { get; set; }
public SaStatus GetStatus() { return SacsSystem.GetStatusForCode(StatusCode); }
}
public AssetCategoryData AssetCategory { get; } = new AssetCategoryData();
//-------------------------------------------------------------------
// Validation.
//-------------------------------------------------------------------
// AssetCategory.
public bool ValidateAssetCategoryData(SbmController_Base.AjaxResponse_Data responseData)
{
// Record disposable items as they are created so all can be disposed when needed.
using (DisposableCollection<SacsHierarchyItem> disposables = new DisposableCollection<SacsHierarchyItem>())
{
// AssetCategory type.
if (AssetCategory.TypeCode != null)
{
String controlName = "AssetCategoryType";
AssetCategory.AssetType = (Sbm.AssetType)Internals.AssetTypeCodes.GetTypeForCode(AssetCategory.TypeCode);
responseData.SetControlValue(controlName, AssetCategory.TypeCode);
if (StringUtil.IsBlank(AssetCategory.TypeCode))
responseData.SetControlError(controlName, Localiser["msg.validation.mandatory.value"]);
else if (AssetCategory.AssetType == 0)
responseData.SetControlError(controlName, Localiser["msg.validation.invalid.value"]);
}
// AssetCategory Code.
if (AssetCategory.Code != null)
{
String controlName = "AssetCategoryCode";
AssetCategory.Code = AssetCategory.Code.ToUpper();
responseData.SetControlValue(controlName, AssetCategory.Code);
if (StringUtil.IsBlank(AssetCategory.Code))
responseData.SetControlError(controlName, Localiser["msg.validation.mandatory.value"]);
else if (StringUtil.GetLength(AssetCategory.Code) > GetControlMaxLength(SbmDbDfn.ColumnIdx.SaAssetCategory_Code))
responseData.SetControlError(controlName, Localiser["msg.validation.maximum.length", GetControlMaxLength(SbmDbDfn.ColumnIdx.SaAssetCategory_Code)]);
}
// AssetCategory DescBrief.
if (AssetCategory.DescBrief != null)
{
String controlName = "AssetCategoryDescription";
responseData.SetControlValue(controlName, AssetCategory.DescBrief);
if (StringUtil.IsBlank(AssetCategory.DescBrief))
responseData.SetControlError(controlName, Localiser["msg.validation.mandatory.value"]);
else if (StringUtil.GetLength(AssetCategory.DescBrief) > GetControlMaxLength(SbmDbDfn.ColumnIdx.SaAssetCategory_DescBrief))
responseData.SetControlError(controlName, Localiser["msg.validation.maximum.length", GetControlMaxLength(SbmDbDfn.ColumnIdx.SaAssetCategory_DescBrief)]);
}
// Notes.
if (AssetCategory.Notes != null)
{
String controlName = "AssetCategoryNotes";
responseData.SetControlValue(controlName, AssetCategory.Notes);
if (StringUtil.GetLength(AssetCategory.Notes) > GetControlMaxLength(SbmDbDfn.ColumnIdx.SaAssetCategory_Notes))
responseData.SetControlError(controlName, Localiser["msg.validation.maximum.length", GetControlMaxLength(SbmDbDfn.ColumnIdx.SaAssetCategory_Notes)]);
}
// AssetCategory status.
if (AssetCategory.StatusCode != null)
{
String controlName = "AssetCategoryStatus";
if (StringUtil.IsBlank(AssetCategory.StatusCode))
responseData.SetControlError(controlName, Localiser["msg.validation.mandatory.value"]);
else if (!SbmAssetCategory.ValidStatuses.Contains(AssetCategory.GetStatus()))
responseData.SetControlError(controlName, Localiser["msg.validation.invalid.value"]);
}
}
return !responseData.HasControlErrors();
}
//-------------------------------------------------------------------
}
//=======================================================================
}
@@ -0,0 +1,130 @@
using DocumentFormat.OpenXml.Spreadsheet;
using Sarand.BusinessMgr.Net.Core.Web.App;
using Sarand.BusinessMgr.Net.Core.Web.Controllers;
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.Helpers;
using Sarand.Net.Standard.Base.Utils;
using System;
namespace Sarand.BusinessMgr.Web.Portal.Models.Assets
{
//=======================================================================
public class Model_VehicleClass : SbmModel_Portal
{
//-------------------------------------------------------------------
// Construction.
public Model_VehicleClass(SbmLocaliser localiser) :
base(localiser)
{
}
//-------------------------------------------------------------------
// Filter properties.
public class FilterData
{
public string Search { get; set; }
public string StatusCat { get; set; }
public SbmVehicleClass.StatusCat GetStatusCat()
{
return EnumUtil.ParseEnum(StatusCat, SbmVehicleClass.StatusCat.NotDeleted);
}
}
public FilterData Filter { get; } = new FilterData();
//-------------------------------------------------------------------
// Item properties.
public class VehicleClassData
{
public string Exists { get; set; }
public string Id { get; set; }
public string MakeCode { get; set; }
public string Model { get; set; }
public string Derivative { get; set; }
public string DescBrief { get; set; }
public string VehicleTypeCode { get; set; }
public Sbm.VehicleType VehicleType { get; set; } = Sbm.VehicleType.Unknown;
public string VehicleCategoryCode { get; set; }
public string FuelTypeCode { get; set; }
public Sbm.VehicleFuelType FuelType { get; set; } = Sbm.VehicleFuelType.Unknown;
public string OdometerUnitsCode { get; set; }
public Sbm.VehicleOdometerUnits OdometerUnits { get; set; } = Sbm.VehicleOdometerUnits.Unknown;
public string Notes { get; set; }
public string StatusCode { get; set; }
public SaStatus GetStatus() { return SacsSystem.GetStatusForCode(StatusCode); }
}
public VehicleClassData VehicleClass { get; } = new VehicleClassData();
//-------------------------------------------------------------------
// Validation.
//-------------------------------------------------------------------
// VehicleClass.
public bool ValidateVehicleClassData(Sacs.Web.Components.SaController.AjaxResponse_Data responseData)
{
// Record disposable items as they are created so all can be disposed when needed.
using (DisposableCollection<SacsHierarchyItem> disposables = new DisposableCollection<SacsHierarchyItem>())
{
// Make.
if (VehicleClass.MakeCode != null)
{
string controlName = "VehicleClassMake";
responseData.SetControlValue(controlName, VehicleClass.MakeCode);
if (VehicleClass.MakeCode.IsBlank())
responseData.SetControlError(controlName, Localiser["msg.validation.mandatory.value"]);
}
// Model.
if (VehicleClass.Model != null)
{
string controlName = "VehicleClassModel";
VehicleClass.Model = VehicleClass.Model.ToUpper();
responseData.SetControlValue(controlName, VehicleClass.Model);
if (VehicleClass.Model.IsBlank())
responseData.SetControlError(controlName, Localiser["msg.validation.mandatory.value"]);
else if (StringUtil.GetLength(VehicleClass.Model) > GetControlMaxLength(SbmDbDfn.ColumnIdx.SaVehicleClass_Model))
responseData.SetControlError(controlName, Localiser["msg.validation.maximum.length", GetControlMaxLength(SbmDbDfn.ColumnIdx.SaVehicleClass_Model)]);
}
// VehicleClass DescBrief.
if (VehicleClass.DescBrief != null)
{
string controlName = "VehicleClassDescription";
responseData.SetControlValue(controlName, VehicleClass.DescBrief);
if (VehicleClass.DescBrief.IsBlank())
responseData.SetControlError(controlName, Localiser["msg.validation.mandatory.value"]);
else if (StringUtil.GetLength(VehicleClass.DescBrief) > GetControlMaxLength(SbmDbDfn.ColumnIdx.SaVehicleClass_DescBrief))
responseData.SetControlError(controlName, Localiser["msg.validation.maximum.length", GetControlMaxLength(SbmDbDfn.ColumnIdx.SaVehicleClass_DescBrief)]);
}
// Notes.
if (VehicleClass.Notes != null)
{
string controlName = "VehicleClassNotes";
responseData.SetControlValue(controlName, VehicleClass.Notes);
if (StringUtil.GetLength(VehicleClass.Notes) > GetControlMaxLength(SbmDbDfn.ColumnIdx.SaVehicleClass_Notes))
responseData.SetControlError(controlName, Localiser["msg.validation.maximum.length", GetControlMaxLength(SbmDbDfn.ColumnIdx.SaVehicleClass_Notes)]);
}
// VehicleClass status.
if (VehicleClass.StatusCode != null)
{
string controlName = "VehicleClassStatus";
if (VehicleClass.StatusCode.IsBlank())
responseData.SetControlError(controlName, Localiser["msg.validation.mandatory.value"]);
else if (!SbmVehicleClass.ValidStatuses.Contains(VehicleClass.GetStatus()))
responseData.SetControlError(controlName, Localiser["msg.validation.invalid.value"]);
}
}
return !responseData.HasControlErrors();
}
//-------------------------------------------------------------------
}
//=======================================================================
}
@@ -0,0 +1,139 @@
using DocumentFormat.OpenXml.Spreadsheet;
using Sarand.BusinessMgr.Net.Core.Web.App;
using Sarand.BusinessMgr.Net.Core.Web.Controllers;
using Sarand.BusinessMgr.Net.Standard.Base.App;
using Sarand.BusinessMgr.Net.Standard.Base.Bus;
using Sarand.BusinessMgr.Net.Standard.Base.Bus.Control;
using Sarand.BusinessMgr.Web.Portal.Models;
using Sarand.Net.Standard.Base.App;
using Sarand.Net.Standard.Base.Bus;
using Sarand.Net.Standard.Base.CodeTypes;
using Sarand.Net.Standard.Base.Core;
using Sarand.Net.Standard.Base.Helpers;
using Sarand.Net.Standard.Base.Utils;
using System;
using System.Collections.Generic;
namespace Sarand.BusinessMgr.Web.Portal.Models.Codes
{
//=======================================================================
public class Model_Code : SbmModel_Portal
{
//-------------------------------------------------------------------
// Construction.
public Model_Code(SbmLocaliser localiser) :
base(localiser)
{
}
//-------------------------------------------------------------------
// Page preparation data.
// Only populated when page is first presented from main code controller.
public void PopulateCodeTypes()
{
if (CodeTypes != null) return;
SaCodeType.Params prmCodeType = new SaCodeType.Params();
prmCodeType.SortColumns.Add(CodeTypeConsole.GetCodeTypeColumnDfn(SaCodeType.Column.Name));
CodeTypes = new List<SaCodeType>(CodeTypeConsole.GetCodeTypeList(prmCodeType));
}
public List<SaCodeType> CodeTypes { get; private set; }
//-------------------------------------------------------------------
// Filter properties.
public class FilterData
{
public String CodeTypeCode { get; set; }
public String Search { get; set; }
public String StatusCat { get; set; }
public SaCode.StatusCat GetStatusCat()
{
return EnumUtil.ParseEnum(StatusCat, SaCode.StatusCat.Any);
}
}
public FilterData Filter { get; } = new FilterData();
//-------------------------------------------------------------------
// Item properties.
public class CodeData
{
public String Exists { get; set; }
public String Id { get; set; }
public String CodeTypeId { get; set; }
public String Code{ get; set; }
public String Description { get; set; }
public String Sequence { get; set; }
public String Notes { get; set; }
public String StatusCode { get; set; }
public SaStatus GetStatus() { return SacsSystem.GetStatusForCode(StatusCode); }
}
public CodeData Code { get; } = new CodeData();
//-------------------------------------------------------------------
// Validation.
//-------------------------------------------------------------------
// Code.
public bool ValidateCodeData(SbmController_Base.AjaxResponse_Data responseData)
{
// Record disposable items as they are created so all can be disposed when needed.
using (DisposableCollection<SacsHierarchyItem> disposables = new DisposableCollection<SacsHierarchyItem>())
{
// Code Type.
if (Code.CodeTypeId != null)
{
String controlName = "CodeType";
if (StringUtil.IsBlank(Code.CodeTypeId))
responseData.SetControlError(controlName, Localiser["msg.validation.mandatory.value"]);
else if (!CodeTypeConsole.CodeTypeExistsById(Code.CodeTypeId.ToSysId()))
responseData.SetControlError(controlName, Localiser["msg.validation.invalid.value"]);
}
// Code.
if (Code.Code != null)
{
String controlName = "CodeCode";
responseData.SetControlValue(controlName, Code.Code);
if (StringUtil.IsBlank(Code.Code))
responseData.SetControlError(controlName, Localiser["msg.validation.mandatory.value"]);
else if (StringUtil.GetLength(Code.Code) > GetControlMaxLength(SaCodeTypeDbDfn.ColumnIdx.SaCode_Code))
responseData.SetControlError(controlName, Localiser["msg.validation.maximum.length", GetControlMaxLength(SaCodeTypeDbDfn.ColumnIdx.SaCode_Code)]);
}
// Description.
if (Code.Description != null)
{
String controlName = "CodeDescription";
responseData.SetControlValue(controlName, Code.Description);
if (StringUtil.IsBlank(Code.Description))
responseData.SetControlError(controlName, Localiser["msg.validation.mandatory.value"]);
else if (StringUtil.GetLength(Code.Description) > GetControlMaxLength(SaCodeTypeDbDfn.ColumnIdx.SaCode_Description))
responseData.SetControlError(controlName, Localiser["msg.validation.maximum.length", GetControlMaxLength(SaCodeTypeDbDfn.ColumnIdx.SaCode_Description)]);
}
// Notes.
if (Code.Notes!= null)
{
String controlName = "CodeNotes";
responseData.SetControlValue(controlName, Code.Notes);
if (StringUtil.GetLength(Code.Notes) > GetControlMaxLength(SaCodeTypeDbDfn.ColumnIdx.SaCode_Notes))
responseData.SetControlError(controlName, Localiser["msg.validation.maximum.length", GetControlMaxLength(SaCodeTypeDbDfn.ColumnIdx.SaCode_Notes)]);
}
// Code status.
if (Code.StatusCode != null)
{
String controlName = "CodeStatus";
if (StringUtil.IsBlank(Code.StatusCode))
responseData.SetControlError(controlName, Localiser["msg.validation.mandatory.value"]);
else if (!SaCode.ValidStatuses.Contains(Code.GetStatus()))
responseData.SetControlError(controlName, Localiser["msg.validation.invalid.value"]);
}
}
return !responseData.HasControlErrors();
}
//-------------------------------------------------------------------
}
//=======================================================================
}
@@ -0,0 +1,124 @@
using DocumentFormat.OpenXml.Spreadsheet;
using Sarand.BusinessMgr.Net.Core.Web.App;
using Sarand.BusinessMgr.Net.Core.Web.Controllers;
using Sarand.BusinessMgr.Net.Standard.Base.App;
using Sarand.BusinessMgr.Net.Standard.Base.Bus;
using Sarand.BusinessMgr.Net.Standard.Base.Bus.Control;
using Sarand.BusinessMgr.Web.Portal.Models;
using Sarand.Net.Standard.Base.App;
using Sarand.Net.Standard.Base.Bus;
using Sarand.Net.Standard.Base.CodeTypes;
using Sarand.Net.Standard.Base.Core;
using Sarand.Net.Standard.Base.Helpers;
using Sarand.Net.Standard.Base.Utils;
using System;
namespace Sarand.BusinessMgr.Web.Portal.Models.Codes
{
//=======================================================================
public class Model_CodeType : SbmModel_Portal
{
//-------------------------------------------------------------------
// Construction.
public Model_CodeType(SbmLocaliser localiser) :
base(localiser)
{
}
//-------------------------------------------------------------------
// Filter properties.
public class FilterData
{
public String Search { get; set; }
public String StatusCat { get; set; }
public SaCodeType.StatusCat GetStatusCat()
{
return EnumUtil.ParseEnum(StatusCat, SaCodeType.StatusCat.ActiveOnly);
}
}
public FilterData Filter { get; } = new FilterData();
//-------------------------------------------------------------------
// Item properties.
public class CodeTypeData
{
public String Exists { get; set; }
public String Id { get; set; }
public String Code { get; set; }
public String Name { get; set; }
public String AutoAssignCode { get; set; }
public String MaxCodeLength { get; set; }
public String Notes { get; set; }
public String StatusCode { get; set; }
public SaStatus GetStatus() { return SacsSystem.GetStatusForCode(StatusCode); }
}
public CodeTypeData CodeType { get; } = new CodeTypeData();
//-------------------------------------------------------------------
// Validation.
//-------------------------------------------------------------------
// CodeType.
public bool ValidateCodeTypeData(SbmController_Base.AjaxResponse_Data responseData)
{
// Record disposable items as they are created so all can be disposed when needed.
using (DisposableCollection<SacsHierarchyItem> disposables = new DisposableCollection<SacsHierarchyItem>())
{
// Code.
if (CodeType.Code != null)
{
String controlName = "CodeTypeCode";
responseData.SetControlValue(controlName, CodeType.Code);
if (StringUtil.IsBlank(CodeType.Code))
responseData.SetControlError(controlName, Localiser["msg.validation.mandatory.value"]);
else if (StringUtil.GetLength(CodeType.Code) > GetControlMaxLength(SaCodeTypeDbDfn.ColumnIdx.SaCodeType_Code))
responseData.SetControlError(controlName, Localiser["msg.validation.maximum.length", GetControlMaxLength(SaCodeTypeDbDfn.ColumnIdx.SaCodeType_Code)]);
else
{
SaCodeType.Params prmCodeType = new SaCodeType.Params();
if (StringUtil.ToBool(CodeType.Exists) && SysId.IsValid(CodeType.Id.ToSysId()))
prmCodeType.IdsExcluded.Add(CodeType.Id.ToSysId());
prmCodeType.Code = CodeType.Code;
if (CodeTypeConsole.GetCodeTypeCount(prmCodeType) > 0)
responseData.SetControlError(controlName, Localiser["msg.validation.duplicate.value"]);
}
}
// Name.
if (CodeType.Name != null)
{
String controlName = "CodeTypeName";
responseData.SetControlValue(controlName, CodeType.Name);
if (StringUtil.IsBlank(CodeType.Name))
responseData.SetControlError(controlName, Localiser["msg.validation.mandatory.value"]);
else if (StringUtil.GetLength(CodeType.Name) > GetControlMaxLength(SaCodeTypeDbDfn.ColumnIdx.SaCodeType_Name))
responseData.SetControlError(controlName, Localiser["msg.validation.maximum.length", GetControlMaxLength(SaCodeTypeDbDfn.ColumnIdx.SaCodeType_Name)]);
}
// Notes.
if (CodeType.Notes != null)
{
String controlName = "CodeTypeNotes";
responseData.SetControlValue(controlName, CodeType.Notes);
if (StringUtil.GetLength(CodeType.Notes) > GetControlMaxLength(SaCodeTypeDbDfn.ColumnIdx.SaCodeType_Notes))
responseData.SetControlError(controlName, Localiser["msg.validation.maximum.length", GetControlMaxLength(SaCodeTypeDbDfn.ColumnIdx.SaCodeType_Notes)]);
}
// CodeType status.
if (CodeType.StatusCode != null)
{
String controlName = "CodeTypeStatus";
if (StringUtil.IsBlank(CodeType.StatusCode))
responseData.SetControlError(controlName, Localiser["msg.validation.mandatory.value"]);
else if (!SaCodeType.ValidStatuses.Contains(CodeType.GetStatus()))
responseData.SetControlError(controlName, Localiser["msg.validation.invalid.value"]);
}
}
return !responseData.HasControlErrors();
}
//-------------------------------------------------------------------
}
//=======================================================================
}
@@ -0,0 +1,113 @@
using Sarand.BusinessMgr.Net.Core.Web.App;
using Sarand.BusinessMgr.Net.Core.Web.Controllers;
using Sarand.BusinessMgr.Net.Standard.Base.App;
using Sarand.BusinessMgr.Net.Standard.Base.Bus;
using Sarand.BusinessMgr.Net.Standard.Base.Helpers;
using Sarand.Net.Standard.Base.App;
using Sarand.Net.Standard.Base.Core;
using Sarand.Net.Standard.Base.Helpers;
using Sarand.Net.Standard.Base.Utils;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace Sarand.BusinessMgr.Web.Portal.Models.Commodities
{
//=======================================================================
public class Model_Commodity : SbmModel_Portal
{
//-------------------------------------------------------------------
// Construction.
public Model_Commodity(SbmLocaliser localiser) :
base(localiser)
{
AdditionalFiles_JS.Add("js/sbm-commodity.js");
}
//-------------------------------------------------------------------
// Filter properties.
public class FilterData
{
public string CommoditySearch { get; set; }
public string CommodityType { get; set; }
}
public FilterData Filter { get; } = new FilterData();
public Sbm.CommodityType GetFilterCommodityType()
{
return (Sbm.CommodityType)Internals.CommodityTypeCodes.GetTypeForCode(Filter.CommodityType);
}
//-------------------------------------------------------------------
// Commodity properties.
public string CommodityId { get; set; }
public string CommodityExists { get; set; }
public string CommodityType { get; set; }
public Sbm.CommodityType GetCommodityType()
{
return (Sbm.CommodityType)Internals.CommodityTypeCodes.GetTypeForCode(CommodityType);
}
public string CommodityCode { get; set; }
public string CommodityDesc { get; set; }
// Common properties.
public string CommodityNotes { get; set; }
public string CommodityStatusCode { get; set; }
public SaStatus CommodityStatus { get { return SacsSystem.GetStatusForCode(CommodityStatusCode); } }
//-------------------------------------------------------------------
// Validation.
//-------------------------------------------------------------------
// Commodity validation.
public bool ValidateCommodityData(Sacs.Web.Components.SaController.AjaxResponse_Data responseData)
{
// Commodity Type.
if (CommodityType != null)
{
if (CommodityType.IsEmpty())
responseData.SetControlError(nameof(CommodityType), Localiser["msg.validation.mandatory.value"]);
}
// Commodity Code.
if (CommodityCode != null)
{
responseData.SetControlValue(nameof(CommodityCode), CommodityCode);
if (CommodityCode.IsEmpty())
responseData.SetControlError(nameof(CommodityCode), Localiser["msg.validation.mandatory.value"]);
if (StringUtil.GetLength(CommodityCode) > GetControlMaxLength(SbmDbDfn.ColumnIdx.Commodity_Code))
responseData.SetControlError(nameof(CommodityCode), Localiser["msg.validation.maximum.length", GetControlMaxLength(SbmDbDfn.ColumnIdx.Commodity_Code)]);
}
// Commodity Desc.
if (CommodityDesc != null)
{
responseData.SetControlValue(nameof(CommodityDesc), CommodityDesc);
if (CommodityDesc.IsEmpty())
responseData.SetControlError(nameof(CommodityDesc), Localiser["msg.validation.mandatory.value"]);
if (StringUtil.GetLength(CommodityDesc) > GetControlMaxLength(SbmDbDfn.ColumnIdx.Commodity_DescBrief))
responseData.SetControlError(nameof(CommodityDesc), Localiser["msg.validation.maximum.length", GetControlMaxLength(SbmDbDfn.ColumnIdx.Commodity_DescBrief)]);
}
// Commodity Notes.
if (CommodityNotes != null)
{
responseData.SetControlValue(nameof(CommodityNotes), CommodityNotes);
if (!CommodityNotes.IsEmpty() && StringUtil.GetLength(CommodityNotes) > GetControlMaxLength(SbmDbDfn.ColumnIdx.Commodity_Notes))
responseData.SetControlError(nameof(CommodityNotes), Localiser["msg.validation.maximum.length", GetControlMaxLength(SbmDbDfn.ColumnIdx.Commodity_Notes)]);
}
// Commodity status.
if (CommodityStatusCode != null)
{
if (CommodityStatusCode.IsEmpty())
responseData.SetControlError(nameof(CommodityStatusCode), Localiser["msg.validation.mandatory.value"]);
else if (!SbmCommodity.ValidStatuses.Contains(CommodityStatus))
responseData.SetControlError(nameof(CommodityStatusCode), Localiser["msg.validation.invalid.value"]);
}
return !responseData.HasControlErrors();
}
//-------------------------------------------------------------------
}
//=======================================================================
}
@@ -1,7 +1,7 @@
using Sarand.BusinessMgr.Net.Core.Web.App;
using System;
namespace Sarand.BusinessMgr.Net.Core.Web.Portal.Models
namespace Sarand.BusinessMgr.Web.Portal.Models
{
//=======================================================================
public class ErrorViewModel : SbmModel_Portal
@@ -12,7 +12,7 @@ using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace Sarand.BusinessMgr.Net.Core.Web.Portal.Models
namespace Sarand.BusinessMgr.Web.Portal.Models
{
//=======================================================================
public class Model_Association : SbmModel_Portal
@@ -1,113 +0,0 @@
using Sarand.BusinessMgr.Net.Core.Web.App;
using Sarand.BusinessMgr.Net.Core.Web.Controllers;
using Sarand.BusinessMgr.Net.Standard.Base.App;
using Sarand.BusinessMgr.Net.Standard.Base.Bus;
using Sarand.BusinessMgr.Net.Standard.Base.Helpers;
using Sarand.Net.Standard.Base.App;
using Sarand.Net.Standard.Base.Core;
using Sarand.Net.Standard.Base.Helpers;
using Sarand.Net.Standard.Base.Utils;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace Sarand.BusinessMgr.Net.Core.Web.Portal.Models
{
//=======================================================================
public class Model_Commodity : SbmModel_Portal
{
//-------------------------------------------------------------------
// Construction.
public Model_Commodity(SbmLocaliser localiser) :
base(localiser)
{
AdditionalFiles_JS.Add("js/sbm-commodity.js");
}
//-------------------------------------------------------------------
// Filter properties.
public class FilterData
{
public String CommoditySearch { get; set; }
public String CommodityType { get; set; }
}
public FilterData Filter { get; } = new FilterData();
public Sbm.CommodityType GetFilterCommodityType()
{
return (Sbm.CommodityType)Internals.CommodityTypeCodes.GetTypeForCode(Filter.CommodityType);
}
//-------------------------------------------------------------------
// Commodity properties.
public String CommodityId { get; set; }
public String CommodityExists { get; set; }
public String CommodityType{ get; set; }
public Sbm.CommodityType GetCommodityType()
{
return (Sbm.CommodityType)Internals.CommodityTypeCodes.GetTypeForCode(CommodityType);
}
public String CommodityCode { get; set; }
public String CommodityDesc { get; set; }
// Common properties.
public String CommodityNotes { get; set; }
public String CommodityStatusCode { get; set; }
public SaStatus CommodityStatus { get { return SacsSystem.GetStatusForCode(CommodityStatusCode); } }
//-------------------------------------------------------------------
// Validation.
//-------------------------------------------------------------------
// Commodity validation.
public bool ValidateCommodityData(SbmController_Web.AjaxResponse_Data responseData)
{
// Commodity Type.
if (CommodityType != null)
{
if (StringUtil.IsEmpty(CommodityType))
responseData.SetControlError(nameof(CommodityType), Localiser["msg.validation.mandatory.value"]);
}
// Commodity Code.
if (CommodityCode != null)
{
responseData.SetControlValue(nameof(CommodityCode), CommodityCode);
if (StringUtil.IsEmpty(CommodityCode))
responseData.SetControlError(nameof(CommodityCode), Localiser["msg.validation.mandatory.value"]);
if (StringUtil.GetLength(CommodityCode) > GetControlMaxLength(SbmDbDfn.ColumnIdx.Commodity_Code))
responseData.SetControlError(nameof(CommodityCode), Localiser["msg.validation.maximum.length", GetControlMaxLength(SbmDbDfn.ColumnIdx.Commodity_Code)]);
}
// Commodity Desc.
if (CommodityDesc != null)
{
responseData.SetControlValue(nameof(CommodityDesc), CommodityDesc);
if (StringUtil.IsEmpty(CommodityDesc))
responseData.SetControlError(nameof(CommodityDesc), Localiser["msg.validation.mandatory.value"]);
if (StringUtil.GetLength(CommodityDesc) > GetControlMaxLength(SbmDbDfn.ColumnIdx.Commodity_DescBrief))
responseData.SetControlError(nameof(CommodityDesc), Localiser["msg.validation.maximum.length", GetControlMaxLength(SbmDbDfn.ColumnIdx.Commodity_DescBrief)]);
}
// Commodity Notes.
if (CommodityNotes != null)
{
responseData.SetControlValue(nameof(CommodityNotes), CommodityNotes);
if (!StringUtil.IsEmpty(CommodityNotes) && StringUtil.GetLength(CommodityNotes) > GetControlMaxLength(SbmDbDfn.ColumnIdx.Commodity_Notes))
responseData.SetControlError(nameof(CommodityNotes), Localiser["msg.validation.maximum.length", GetControlMaxLength(SbmDbDfn.ColumnIdx.Commodity_Notes)]);
}
// Commodity status.
if (CommodityStatusCode != null)
{
if (StringUtil.IsEmpty(CommodityStatusCode))
responseData.SetControlError(nameof(CommodityStatusCode), Localiser["msg.validation.mandatory.value"]);
else if (!SbmCommodity.ValidStatuses.Contains(CommodityStatus))
responseData.SetControlError(nameof(CommodityStatusCode), Localiser["msg.validation.invalid.value"]);
}
return !responseData.HasControlErrors();
}
//-------------------------------------------------------------------
}
//=======================================================================
}
@@ -12,7 +12,7 @@ using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace Sarand.BusinessMgr.Net.Core.Web.Portal.Models
namespace Sarand.BusinessMgr.Web.Portal.Models
{
//=======================================================================
public class Model_Contact : SbmModel_Portal
@@ -9,7 +9,7 @@ using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace Sarand.BusinessMgr.Net.Core.Web.Portal.Models
namespace Sarand.BusinessMgr.Web.Portal.Models
{
//=======================================================================
public class Model_Home : SbmModel_Portal
@@ -10,7 +10,7 @@ using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace Sarand.BusinessMgr.Net.Core.Web.Portal.Models
namespace Sarand.BusinessMgr.Web.Portal.Models
{
//=======================================================================
public class Model_Security : SbmModel_Portal
@@ -1,11 +1,14 @@
using Sarand.BusinessMgr.Net.Core.Web.App;
using Sarand.BusinessMgr.Net.Core.Web.Models;
using Sarand.BusinessMgr.Net.Standard.Base.App;
using Sarand.BusinessMgr.Net.Standard.Base.Core;
using Sarand.BusinessMgr.Web.Portal.App;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace Sarand.BusinessMgr.Net.Core.Web.Portal.Models
namespace Sarand.BusinessMgr.Web.Portal.Models
{
//=======================================================================
public class SbmModel_Portal : SbmModel_Base
@@ -19,7 +22,9 @@ namespace Sarand.BusinessMgr.Net.Core.Web.Portal.Models
//-------------------------------------------------------------------
// Properties.
public String OrganisationName { get { return SessionData?.Organisation?.DisplayName ?? ""; } }
public SbmSessionData_Portal SessionData_Portal { get { return SessionData as SbmSessionData_Portal; } }
public override ISbmUserContext_Core UserContext_Core { get { return SessionData_Portal; } }
public String OrganisationName { get { return SessionData_Portal?.Organisation?.DisplayName ?? ""; } }
//-------------------------------------------------------------------
}
@@ -3,8 +3,8 @@
<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
<CopyRefAssembliesToPublishDirectory>false</CopyRefAssembliesToPublishDirectory>
<RootNamespace>Sarand.BusinessMgr.Net.Core.Web</RootNamespace>
<AssemblyName>Sarand.BusinessMgr.Net.Core.Web</AssemblyName>
<RootNamespace>Sarand.BusinessMgr.Web</RootNamespace>
<AssemblyName>Sarand.BusinessMgr.Web</AssemblyName>
<Description>SaBusinessMgrCoreWebPortal</Description>
<PackageId>SaBusinessMgrCoreWebPortal</PackageId>
<Authors>Sarand</Authors>
@@ -28,6 +28,10 @@
<ProjectReference Include="..\SaBusMgrStandardDbas\SaBusMgrStandardDbas.csproj" />
</ItemGroup>
<ItemGroup>
<EmbeddedResource Include="Resources\**\*.*" />
</ItemGroup>
<ItemGroup>
<Content Update="appsettings.Machine.Sarand110.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
@@ -37,7 +41,17 @@
</ItemGroup>
<ItemGroup>
<Folder Include="Portal\Controllers\Vehicles\" />
<Folder Include="Views\Assets\Asset\" />
<Folder Include="Views\Assets\Vehicle\" />
<Folder Include="Views\Vehicles\" />
<Folder Include="Views\Transactions\" />
</ItemGroup>
<ItemGroup>
<EmbeddedResource Update="Localisation\SbmPortalProperties.resx">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</EmbeddedResource>
</ItemGroup>
</Project>
@@ -0,0 +1,215 @@
@* Assets - Asset *@
@model Model_Asset
@inject SbmLocaliser Localiser
@{
ViewData["Title"] = "Assets";
Model.AdditionalFiles_JS.Add("js/sbm-asset.js");
}
<div class="sbm-panel Assets">
<script type="text/javascript">
sbm.dlg.title.createItem = '@(Localiser.GetJavascriptText("caption.asset.create"))';
sbm.dlg.title.updateItem = '@(Localiser.GetJavascriptText("caption.asset.update"))';
sbm.lbl.selectCategory = '@(Localiser.GetJavascriptText("caption.category.select"))';
</script>
<div class="sbm-panel-data">
@* Filter form - wrap header to include paging controls. *@
@using (@Html.BeginForm(FormMethod.Post, new { @id = "frmAssetFilter", @class = "sbm-form", @onSubmit = "return false;" }))
{
<div class="sbm-panel-header">
<div class="row">
<div class="fixed">
<partial name="~/Views/Common/ResultsPaging.cshtml" />
</div>
<div class="col center"><div class="sbm-panel-caption">@Localiser["label.assets"]</div></div>
<div class="fixed">
@* Burger menu *@
<ul class="nav navbar-nav navbar-dark navbar-right sbm-burger">
<li class="dropdown-toggle caret-off">
<a href="#" class="dropdown" data-toggle="dropdown" role="button" aria-haspopup="true" aria-expanded="false"><span class="navbar-toggler-icon"></span></a>
<ul class="dropdown-menu dropdown-menu-right sbm-dropdown-menu">@* Menu items will be inserted here. *@</ul>
</li>
</ul>
</div>
</div>
</div>
@* Filter Panel *@
<div class="sbm-panel-filter">
<div class="row pb4">
<div class="col-sm-5 sbm-label text-left text-sm-right">@Localiser["label.search"]</div>
<div class="col-sm-7">
@Html.TextBoxFor(model => model.Filter.Search, new { @id = "", @class = "FilterSearch", @maxlength = "32" })
</div>
</div>
</div>
}
<div class="sbm-panel-item-table">
<table class="sbm-item-list">
<thead>
<tr>
<th class="pl-2">@Localiser["label.category"]</th>
<th class="">@Localiser["label.serial"]</th>
<th class="">@Localiser["label.description"]</th>
<th class="center">@Localiser["label.status"]</th>
<th class="right" style="width:2%;"></th>
</tr>
<tr><td colspan="6"><div class="sbm-panel-messages"></div></td></tr>
</thead>
<tbody></tbody>
</table>
</div>
@* Element templates *@
<div class="sbm-panel-item-template">
@* Filter Burger option template - ul is ignored *@
<ul class="sbm-dropdown-menu AssetFilter">
<li class="sbm-dropdown-item sbm-action"><div class="sbm-label">XXX</div></li>
</ul>
@* Asset row template - table is ignored. *@
<table class="">
<tr class="sbm-row-item Asset">
<td><div class="AssetCategory pl-1"></div></td>
<td><div class="AssetSerial"></div></td>
<td><div class="AssetDescription"></div></td>
<td><div class="AssetStatusDesc center"></div></td>
<td>
<div class="right">
@* Burger menu *@
<ul class="nav navbar-nav navbar-light navbar-right sbm-burger">
<li class="dropdown-toggle caret-off">
<a href="#" class="dropdown" data-toggle="dropdown" role="button" aria-haspopup="true" aria-expanded="false"><span class="sbm-ellipsis-dot"></span><span class="sbm-ellipsis-dot"></span><span class="sbm-ellipsis-dot"></span></a>
<ul class="dropdown-menu dropdown-menu-right sbm-dropdown-menu">@* Menu items will be inserted here. *@</ul>
</li>
</ul>
</div>
</td>
</tr>
</table>
@* Item Burger option template - ul is ignored *@
<ul class="sbm-dropdown-menu Asset">
<li class="sbm-dropdown-item sbm-action"><div class="sbm-label">XXX</div></li>
</ul>
</div>
@using (@Html.BeginForm(FormMethod.Post, new { @id = "frmAssetId", @onSubmit = "return false;" }))
{
@Html.HiddenFor(model => model.Asset.Id, new { @id = "", @class = "AssetId" })
}
@using (@Html.BeginForm(FormMethod.Post, new { @id = "frmCategories", @onSubmit = "return false;" }))
{
}
<div id="dlgAsset" class="sbm-dialog">
<div class="spacer2"></div>
@using (@Html.BeginForm(FormMethod.Post, new { @id = "frmAsset", @class = "sbm-form", @onSubmit = "return false;" }))
{
@Html.HiddenFor(model => model.Asset.Exists, new { @id = "", @class = "AssetExists" })
@Html.HiddenFor(model => model.Asset.Id, new { @id = "", @class = "AssetId" })
@* Type *@
<div class="sbm-form-control AssetCategory">
<div class="row">
<div class="col-sm-3">
<div class="sbm-label">
<label for=""><span>@Localiser["label.category"]</span><span class="required">&nbsp;*</span></label>
</div>
</div>
<div class="col-sm-9">
<div class="sbc-control row">
<div class="col">
@Html.HiddenFor(model => model.Asset.CategoryId, new { @id = "", @class = "AssetCategoryId" })
<input type="text" class="AssetCategory sbm-input" readonly="readonly" />
</div>
<div class="fixed">
<div class="sbc-action AssetCategorySearch"><img src="~/img/action/search24.png" alt="" title="" /></div>
</div>
</div>
<div class="sbm-message"></div>
</div>
</div>
</div>
@* Serial Number*@
<div class="sbm-form-control AssetSerialNumber">
<div class="row">
<div class="col-sm-3">
<div class="sbm-label">
<label for="ifAssetSerialNumber"><span>@Localiser["label.serial"]</span><span class="required">&nbsp;*</span></label>
</div>
</div>
<div class="col-sm-9">
<div class="sbm-control">
@Html.TextBoxFor(model => model.Asset.SerialNumber, new { @id = "ifAssetSerialNumber", @class = "AssetSerialNumber sbm-input upper", @maxlength = @Model.GetControlMaxLength(SbmDbDfn.ColumnIdx.SaAsset_SerialNumber) })
</div>
<div class="sbm-message"></div>
</div>
</div>
</div>
@* Description *@
<div class="sbm-form-control AssetDescription">
<div class="row">
<div class="col-sm-3">
<div class="sbm-label">
<label for="ifAssetDescription"><span>@Localiser["label.description"]</span></label>
</div>
</div>
<div class="col-sm-9">
<div class="sbm-control">
@Html.TextBoxFor(model => model.Asset.DescBrief, new { @id = "ifAssetDescription", @class = "AssetDescription sbm-input", @maxlength = @Model.GetControlMaxLength(SbmDbDfn.ColumnIdx.SaAsset_DescBrief) })
</div>
<div class="sbm-message"></div>
</div>
</div>
</div>
@* Status *@
<div class="sbm-form-control AssetStatus">
<div class="row">
<div class="col-sm-3">
<div class="sbm-label">
<label for="cbAssetStatus"><span>@Localiser["label.status"]</span><span class="required">&nbsp;*</span></label>
</div>
</div>
<div class="col-sm-9">
<div class="sbm-control">
<select id="cbAssetStatus" class="sbm-select AssetStatus" name="Asset.StatusCode"></select>
</div>
<div class="sbm-message"></div>
</div>
</div>
</div>
@* Notes *@
<div class="sbm-form-control AssetNotes">
<div class="row">
<div class="col-sm-3">
<div class="sbm-label">
<label for="ifAssetNotes"><span>@Localiser["label.notes"]</span></label>
</div>
</div>
<div class="col-sm">
<div class="sbm-control">
@Html.TextAreaFor(model => model.Asset.Notes, new { @id = "ifAssetNotes", @class = "AssetNotes sbm-textarea", @rows = 3, @maxlength = @Model.GetControlMaxLength(SbmDbDfn.ColumnIdx.SaAsset_Notes) })
</div>
<div class="sbm-message"></div>
</div>
</div>
</div>
}
<div class="spacer2"></div>
</div>
@* Select Item dialog and script *@
<partial name="~/Views/Common/SelectItem.cshtml" />
</div>
</div>
@@ -0,0 +1,202 @@
@* Assets - Asset Category *@
@model Model_AssetCategory
@inject SbmLocaliser Localiser
@{
ViewData["Title"] = "Asset Categories";
Model.AdditionalFiles_JS.Add("js/sbm-asset-cat.js");
}
<div class="sbm-panel AssetCategories">
<script type="text/javascript">
sbm.dlg.title.createItem = '@(Localiser.GetJavascriptText("caption.asset.category.create"))';
sbm.dlg.title.updateItem = '@(Localiser.GetJavascriptText("caption.asset.category.update"))';
</script>
<div class="sbm-panel-data">
@* Filter form - wrap header to include paging controls. *@
@using (@Html.BeginForm(FormMethod.Post, new { @id = "frmAssetCategoryFilter", @class = "sbm-form", @onSubmit = "return false;" }))
{
<div class="sbm-panel-header">
<div class="row">
<div class="fixed">
<partial name="~/Views/Common/ResultsPaging.cshtml" />
</div>
<div class="col center"><div class="sbm-panel-caption">@Localiser["caption.asset.categories"]</div></div>
<div class="fixed">
@* Burger menu *@
<ul class="nav navbar-nav navbar-dark navbar-right sbm-burger">
<li class="dropdown-toggle caret-off">
<a href="#" class="dropdown" data-toggle="dropdown" role="button" aria-haspopup="true" aria-expanded="false"><span class="navbar-toggler-icon"></span></a>
<ul class="dropdown-menu dropdown-menu-right sbm-dropdown-menu">@* Menu items will be inserted here. *@</ul>
</li>
</ul>
</div>
</div>
</div>
@* Filter Panel *@
<div class="sbm-panel-filter">
<div class="row pb4">
<div class="col-sm-5 sbm-label text-left text-sm-right">@Localiser["label.search"]</div>
<div class="col-sm-7">
@Html.TextBoxFor(model => model.Filter.Search, new { @id = "", @class = "FilterSearch", @maxlength = "32" })
</div>
</div>
</div>
}
<div class="sbm-panel-item-table">
<table class="sbm-item-list">
<thead>
<tr>
<th class="pl-2">@Localiser["label.type"]</th>
<th class="">@Localiser["label.code"]</th>
<th class="">@Localiser["label.description"]</th>
<th class="center">@Localiser["label.status"]</th>
<th class="right" style="width:2%;"></th>
</tr>
<tr><td colspan="6"><div class="sbm-panel-messages"></div></td></tr>
</thead>
<tbody></tbody>
</table>
</div>
@* Element templates *@
<div class="sbm-panel-item-template">
@* Filter Burger option template - ul is ignored *@
<ul class="sbm-dropdown-menu AssetCategoryFilter">
<li class="sbm-dropdown-item sbm-action"><div class="sbm-label">XXX</div></li>
</ul>
@* AssetCategory row template - table is ignored. *@
<table class="">
<tr class="sbm-row-item AssetCategory">
<td><div class="AssetCategoryType pl-1"></div></td>
<td><div class="AssetCategoryCode"></div></td>
<td><div class="AssetCategoryDescription"></div></td>
<td><div class="AssetCategoryStatusDesc center"></div></td>
<td>
<div class="right">
@* Burger menu *@
<ul class="nav navbar-nav navbar-light navbar-right sbm-burger">
<li class="dropdown-toggle caret-off">
<a href="#" class="dropdown" data-toggle="dropdown" role="button" aria-haspopup="true" aria-expanded="false"><span class="sbm-ellipsis-dot"></span><span class="sbm-ellipsis-dot"></span><span class="sbm-ellipsis-dot"></span></a>
<ul class="dropdown-menu dropdown-menu-right sbm-dropdown-menu">@* Menu items will be inserted here. *@</ul>
</li>
</ul>
</div>
</td>
</tr>
</table>
@* Item Burger option template - ul is ignored *@
<ul class="sbm-dropdown-menu AssetCategory">
<li class="sbm-dropdown-item sbm-action"><div class="sbm-label">XXX</div></li>
</ul>
</div>
@using (@Html.BeginForm(FormMethod.Post, new { @id = "frmAssetCategoryId", @onSubmit = "return false;" }))
{
@Html.HiddenFor(model => model.AssetCategory.Id, new { @id = "", @class = "AssetCategoryId" })
}
<div id="dlgAssetCategory" class="sbm-dialog">
<div class="spacer2"></div>
@using (@Html.BeginForm(FormMethod.Post, new { @id = "frmAssetCategory", @class = "sbm-form", @onSubmit = "return false;" }))
{
@Html.HiddenFor(model => model.AssetCategory.Exists, new { @id = "", @class = "AssetCategoryExists" })
@Html.HiddenFor(model => model.AssetCategory.Id, new { @id = "", @class = "AssetCategoryId" })
@* Type *@
<div class="sbm-form-control AssetCategoryType">
<div class="row">
<div class="col-sm-3">
<div class="sbm-label">
<label for="ifAssetCategoryType"><span>@Localiser["label.type"]</span><span class="required">&nbsp;*</span></label>
</div>
</div>
<div class="col-sm-9">
<div class="sbm-control">
@Html.DropDownListFor(model => model.AssetCategory.TypeCode, Model.GetAssetTypeCodesForSelect(), Localiser["label.select"], new { @id = "ifAssetCategoryType", @class = "AssetCategoryType sbm-select" })
</div>
<div class="sbm-message"></div>
</div>
</div>
</div>
@* Contact Code *@
<div class="sbm-form-control AssetCategoryCode">
<div class="row">
<div class="col-sm-3">
<div class="sbm-label">
<label for="ifAssetCategoryCode"><span>@Localiser["label.code"]</span><span class="required">&nbsp;*</span></label>
</div>
</div>
<div class="col-sm-9">
<div class="sbm-control">
@Html.TextBoxFor(model => model.AssetCategory.Code, new { @id = "ifAssetCategoryCode", @class = "AssetCategoryCode sbm-input upper", @maxlength = @Model.GetControlMaxLength(SbmDbDfn.ColumnIdx.SaAssetCategory_Code) })
</div>
<div class="sbm-message"></div>
</div>
</div>
</div>
@* Description *@
<div class="sbm-form-control AssetCategoryDescription">
<div class="row">
<div class="col-sm-3">
<div class="sbm-label">
<label for="ifAssetCategoryDescription"><span>@Localiser["label.description"]</span><span class="required">&nbsp;*</span></label>
</div>
</div>
<div class="col-sm-9">
<div class="sbm-control">
@Html.TextBoxFor(model => model.AssetCategory.DescBrief, new { @id = "ifAssetCategoryDescription", @class = "AssetCategoryDescription sbm-input", @maxlength = @Model.GetControlMaxLength(SbmDbDfn.ColumnIdx.SaAssetCategory_DescBrief) })
</div>
<div class="sbm-message"></div>
</div>
</div>
</div>
@* Status *@
<div class="sbm-form-control AssetCategoryStatus">
<div class="row">
<div class="col-sm-3">
<div class="sbm-label">
<label for="cbAssetCategoryStatus"><span>@Localiser["label.status"]</span><span class="required">&nbsp;*</span></label>
</div>
</div>
<div class="col-sm-9">
<div class="sbm-control">
<select id="cbAssetCategoryStatus" class="sbm-select AssetCategoryStatus" name="AssetCategory.StatusCode"></select>
</div>
<div class="sbm-message"></div>
</div>
</div>
</div>
@* Notes *@
<div class="sbm-form-control AssetCategoryNotes">
<div class="row">
<div class="col-sm-3">
<div class="sbm-label">
<label for="ifAssetCategoryNotes"><span>@Localiser["label.notes"]</span></label>
</div>
</div>
<div class="col-sm">
<div class="sbm-control">
@Html.TextAreaFor(model => model.AssetCategory.Notes, new { @id = "ifAssetCategoryNotes", @class = "AssetCategoryNotes sbm-textarea", @rows = 3, @maxlength = @Model.GetControlMaxLength(SbmDbDfn.ColumnIdx.SaAssetCategory_Notes) })
</div>
<div class="sbm-message"></div>
</div>
</div>
</div>
}
<div class="spacer2"></div>
</div>
</div>
</div>
@@ -0,0 +1,202 @@
@* Vehicles - Vehicle Class *@
@model Model_VehicleClass
@inject SbmLocaliser Localiser
@{
ViewData["Title"] = "Vehicle Classes";
Model.AdditionalFiles_JS.Add("js/sbm-veh-cls.js");
}
<div class="sbm-panel VehicleClasses">
<script type="text/javascript">
sbm.dlg.title.createItem = '@(Localiser.GetJavascriptText("caption.vehicle.class.create"))';
sbm.dlg.title.updateItem = '@(Localiser.GetJavascriptText("caption.vehicle.class.update"))';
</script>
<div class="sbm-panel-data">
@* Filter form - wrap header to include paging controls. *@
@using (@Html.BeginForm(FormMethod.Post, new { @id = "frmVehicleClassFilter", @class = "sbm-form", @onSubmit = "return false;" }))
{
<div class="sbm-panel-header">
<div class="row">
<div class="fixed">
<partial name="~/Views/Common/ResultsPaging.cshtml" />
</div>
<div class="col center"><div class="sbm-panel-caption">@Localiser["caption.vehicle.classes"]</div></div>
<div class="fixed">
@* Burger menu *@
<ul class="nav navbar-nav navbar-dark navbar-right sbm-burger">
<li class="dropdown-toggle caret-off">
<a href="#" class="dropdown" data-toggle="dropdown" role="button" aria-haspopup="true" aria-expanded="false"><span class="navbar-toggler-icon"></span></a>
<ul class="dropdown-menu dropdown-menu-right sbm-dropdown-menu">@* Menu items will be inserted here. *@</ul>
</li>
</ul>
</div>
</div>
</div>
@* Filter Panel *@
<div class="sbm-panel-filter">
<div class="row pb4">
<div class="col-sm-5 sbm-label text-left text-sm-right">@Localiser["label.search"]</div>
<div class="col-sm-7">
@Html.TextBoxFor(model => model.Filter.Search, new { @id = "", @class = "FilterSearch", @maxlength = "32" })
</div>
</div>
</div>
}
<div class="sbm-panel-item-table">
<table class="sbm-item-list">
<thead>
<tr>
<th class="pl-2">@Localiser["label.make"]</th>
<th class="">@Localiser["label.model"]</th>
<th class="">@Localiser["label.derivative"]</th>
<th class="center">@Localiser["label.status"]</th>
<th class="right" style="width:2%;"></th>
</tr>
<tr><td colspan="6"><div class="sbm-panel-messages"></div></td></tr>
</thead>
<tbody></tbody>
</table>
</div>
@* Element templates *@
<div class="sbm-panel-item-template">
@* Filter Burger option template - ul is ignored *@
<ul class="sbm-dropdown-menu VehicleClassFilter">
<li class="sbm-dropdown-item sbm-action"><div class="sbm-label">XXX</div></li>
</ul>
@* VehicleClass row template - table is ignored. *@
<table class="">
<tr class="sbm-row-item VehicleClass">
<td><div class="VehicleClassMake pl-1"></div></td>
<td><div class="VehicleClassModel"></div></td>
<td><div class="VehicleClassDerivative"></div></td>
<td><div class="VehicleClassStatusDesc center"></div></td>
<td>
<div class="right">
@* Burger menu *@
<ul class="nav navbar-nav navbar-light navbar-right sbm-burger">
<li class="dropdown-toggle caret-off">
<a href="#" class="dropdown" data-toggle="dropdown" role="button" aria-haspopup="true" aria-expanded="false"><span class="sbm-ellipsis-dot"></span><span class="sbm-ellipsis-dot"></span><span class="sbm-ellipsis-dot"></span></a>
<ul class="dropdown-menu dropdown-menu-right sbm-dropdown-menu">@* Menu items will be inserted here. *@</ul>
</li>
</ul>
</div>
</td>
</tr>
</table>
@* Item Burger option template - ul is ignored *@
<ul class="sbm-dropdown-menu VehicleClass">
<li class="sbm-dropdown-item sbm-action"><div class="sbm-label">XXX</div></li>
</ul>
</div>
@using (@Html.BeginForm(FormMethod.Post, new { @id = "frmVehicleClassId", @onSubmit = "return false;" }))
{
@Html.HiddenFor(model => model.VehicleClass.Id, new { @id = "", @class = "VehicleClassId" })
}
<div id="dlgVehicleClass" class="sbm-dialog">
<div class="spacer2"></div>
@using (@Html.BeginForm(FormMethod.Post, new { @id = "frmVehicleClass", @class = "sbm-form", @onSubmit = "return false;" }))
{
@Html.HiddenFor(model => model.VehicleClass.Exists, new { @id = "", @class = "VehicleClassExists" })
@Html.HiddenFor(model => model.VehicleClass.Id, new { @id = "", @class = "VehicleClassId" })
@* Make *@
<div class="sbm-form-control VehicleClassMake">
<div class="row">
<div class="col-sm-3">
<div class="sbm-label">
<label for="cbVehicleClassMake"><span>@Localiser["label.make"]</span><span class="required">&nbsp;*</span></label>
</div>
</div>
<div class="col-sm-9">
<div class="sbm-control">
<select id="cbVehicleClassMake" name="VehicleClass.MakeCode" class="VehicleClassMake sbm-select"></select>
</div>
<div class="sbm-message"></div>
</div>
</div>
</div>
@* Contact Model *@
<div class="sbm-form-control VehicleClassModel">
<div class="row">
<div class="col-sm-3">
<div class="sbm-label">
<label for="ifVehicleClassModel"><span>@Localiser["label.model"]</span><span class="required">&nbsp;*</span></label>
</div>
</div>
<div class="col-sm-9">
<div class="sbm-control">
@Html.TextBoxFor(model => model.VehicleClass.Model, new { @id = "ifVehicleClassModel", @class = "VehicleClassModel sbm-input upper", @maxlength = @Model.GetControlMaxLength(SbmDbDfn.ColumnIdx.SaVehicleClass_Model) })
</div>
<div class="sbm-message"></div>
</div>
</div>
</div>
@* Derivative *@
<div class="sbm-form-control VehicleClassDerivative">
<div class="row">
<div class="col-sm-3">
<div class="sbm-label">
<label for="ifVehicleClassDerivative"><span>@Localiser["label.description"]</span><span class="required">&nbsp;*</span></label>
</div>
</div>
<div class="col-sm-9">
<div class="sbm-control">
@Html.TextBoxFor(model => model.VehicleClass.DescBrief, new { @id = "ifVehicleClassDerivative", @class = "VehicleClassDerivative sbm-input", @maxlength = @Model.GetControlMaxLength(SbmDbDfn.ColumnIdx.SaVehicleClass_DescBrief) })
</div>
<div class="sbm-message"></div>
</div>
</div>
</div>
@* Status *@
<div class="sbm-form-control VehicleClassStatus">
<div class="row">
<div class="col-sm-3">
<div class="sbm-label">
<label for="cbVehicleClassStatus"><span>@Localiser["label.status"]</span><span class="required">&nbsp;*</span></label>
</div>
</div>
<div class="col-sm-9">
<div class="sbm-control">
<select id="cbVehicleClassStatus" class="sbm-select VehicleClassStatus" name="VehicleClass.StatusCode"></select>
</div>
<div class="sbm-message"></div>
</div>
</div>
</div>
@* Notes *@
<div class="sbm-form-control VehicleClassNotes">
<div class="row">
<div class="col-sm-3">
<div class="sbm-label">
<label for="ifVehicleClassNotes"><span>@Localiser["label.notes"]</span></label>
</div>
</div>
<div class="col-sm">
<div class="sbm-control">
@Html.TextAreaFor(model => model.VehicleClass.Notes, new { @id = "ifVehicleClassNotes", @class = "VehicleClassNotes sbm-textarea", @rows = 3, @maxlength = @Model.GetControlMaxLength(SbmDbDfn.ColumnIdx.SaVehicleClass_Notes) })
</div>
<div class="sbm-message"></div>
</div>
</div>
</div>
}
<div class="spacer2"></div>
</div>
</div>
</div>
@@ -8,9 +8,6 @@
<div class="sbm-panel Associations">
<script type="text/javascript">
sbm.lbl.refresh = '@(Localiser.GetJavascriptText("label.refresh"))';
sbm.lbl.create = '@(Localiser.GetJavascriptText("label.create"))';
sbm.lbl.edit = '@(Localiser.GetJavascriptText("label.edit"))';
</script>
<div class="row">
@@ -0,0 +1,240 @@
@* Codes - Code *@
@model Model_Code
@inject SbmLocaliser Localiser
@{
ViewData["Title"] = "Codes";
Model.AdditionalFiles_JS.Add("js/sbm-cod.js");
}
<div class="sbm-panel Codes fullheight">
<script type="text/javascript">
sbm.dlg.title.createItem = '@(Localiser.GetJavascriptText("caption.code.create"))';
sbm.dlg.title.updateItem = '@(Localiser.GetJavascriptText("caption.code.update"))';
</script>
<div class="sbm-panel-data sbm-flex">
@* Filter form - wrap header to include paging controls. *@
@using (@Html.BeginForm(FormMethod.Post, new { @id = "frmCodeFilter", @class = "sbm-form", @onSubmit = "return false;" }))
{
<div class="sbm-panel-header">
<div class="row">
<div class="fixed">
<partial name="~/Views/Common/ResultsPaging.cshtml" />
</div>
<div class="col center"><div class="sbm-panel-caption">@Localiser["label.codes"]</div></div>
<div class="fixed">
@* Burger menu *@
<ul class="nav navbar-nav navbar-dark navbar-right sbm-burger">
<li class="dropdown-toggle caret-off">
<a href="#" class="dropdown" data-toggle="dropdown" role="button" aria-haspopup="true" aria-expanded="false"><span class="navbar-toggler-icon"></span></a>
<ul class="dropdown-menu dropdown-menu-right sbm-dropdown-menu">@* Menu items will be inserted here. *@</ul>
</li>
</ul>
</div>
</div>
</div>
@* Filter Panel *@
<div class="sbm-panel-filter">
<div class="row pb4">
<div class="col-md-6 row">
<div class="col-sm-5 sbm-label text-left text-sm-right">@Localiser["label.code.type"]</div>
<div class="col-sm-7">
@Html.DropDownListFor(model => model.Filter.CodeTypeCode, Model.GetCodeTypesForSelect(), new { @id = "", @class = "FilterCodeTypeCode", @maxlength = "32" })
</div>
</div>
<div class="col-md-6 row">
<div class="col-sm-5 sbm-label text-left text-sm-right">@Localiser["label.search"]</div>
<div class="col-sm-7">
@Html.TextBoxFor(model => model.Filter.Search, new { @id = "", @class = "FilterSearch", @maxlength = "32" })
</div>
</div>
</div>
</div>
}
<div class="sbm-panel-item-table sbm-flex">
<table class="sbm-item-list">
<thead>
<tr>
<th class="pl-2" style="width:35%">@Localiser["label.type"]</th>
<th class="" style="width:15%">@Localiser["label.code"]</th>
<th class="" style="width:35%">@Localiser["label.description"]</th>
<th class="center">@Localiser["label.status"]</th>
<th class="right" style="width:2%;"></th>
</tr>
<tr><td colspan="6"><div class="sbm-panel-messages"></div></td></tr>
</thead>
<tbody></tbody>
</table>
</div>
</div>
@* Element templates *@
<div class="sbm-panel-item-template">
@* Filter Burger option template - ul is ignored *@
<ul class="sbm-dropdown-menu CodeFilter">
<li class="sbm-dropdown-item sbm-action"><div class="sbm-label">XXX</div></li>
</ul>
@* Code row template - table is ignored. *@
<table class="">
<tr class="sbm-row-item Code">
<td><div class="CodeType pl-1"></div></td>
<td><div class="CodeCode "></div></td>
<td><div class="CodeDesc"></div></td>
<td><div class="CodeStatusDesc center"></div></td>
<td>
<div class="right">
@* Burger menu *@
<ul class="nav navbar-nav navbar-light navbar-right sbm-burger">
<li class="dropdown-toggle caret-off">
<a href="#" class="dropdown" data-toggle="dropdown" role="button" aria-haspopup="true" aria-expanded="false"><span class="sbm-ellipsis-dot"></span><span class="sbm-ellipsis-dot"></span><span class="sbm-ellipsis-dot"></span></a>
<ul class="dropdown-menu dropdown-menu-right sbm-dropdown-menu">@* Menu items will be inserted here. *@</ul>
</li>
</ul>
</div>
</td>
</tr>
</table>
@* Item Burger option template - ul is ignored *@
<ul class="sbm-dropdown-menu Code">
<li class="sbm-dropdown-item sbm-action"><div class="sbm-label">XXX</div></li>
</ul>
</div>
@using (@Html.BeginForm(FormMethod.Post, new { @id = "frmCodeId", @onSubmit = "return false;" }))
{
@Html.HiddenFor(model => model.Code.Id, new { @id = "", @class = "CodeId" })
}
@using (@Html.BeginForm(FormMethod.Post, new { @id = "frmCategories", @onSubmit = "return false;" }))
{
}
<div id="dlgCode" class="sbm-dialog">
<div class="spacer2"></div>
@using (@Html.BeginForm(FormMethod.Post, new { @id = "frmCode", @class = "sbm-form", @onSubmit = "return false;" }))
{
@Html.HiddenFor(model => model.Code.Exists, new { @id = "", @class = "CodeExists" })
@Html.HiddenFor(model => model.Code.Id, new { @id = "", @class = "CodeId" })
@* Type *@
<div class="sbm-form-control CodeType">
<div class="row">
<div class="col-sm-3">
<div class="sbm-label">
<label for=""><span>@Localiser["label.type"]</span><span class="required">&nbsp;*</span></label>
</div>
</div>
<div class="col-sm-9">
<div class="sbc-control">
<select name="model.Code.CodeTypeId" class="CodeType sbm-select" readonly="readonly"></select>
</div>
<div class="sbm-message"></div>
</div>
</div>
</div>
@* Code *@
<div class="sbm-form-control CodeCode">
<div class="row">
<div class="col-sm-3">
<div class="sbm-label">
<label for="ifCodeCode"><span>@Localiser["label.code"]</span><span class="required">&nbsp;*</span></label>
</div>
</div>
<div class="col-sm-9">
<div class="sbm-control">
@Html.TextBoxFor(model => model.Code.Code, new { @id = "ifCodeCode", @class = "CodeCode sbm-input", @maxlength = 3 })
</div>
<div class="sbm-message"></div>
</div>
</div>
</div>
@* Sequence *@
<div class="sbm-form-control CodeSequence">
<div class="row">
<div class="col-sm-3">
<div class="sbm-label">
<label for="ifCodeSequence"><span>@Localiser["label.sequence"]</span><span class="required">&nbsp;*</span></label>
</div>
</div>
<div class="col-sm-9">
<div class="sbm-control">
@Html.TextBoxFor(model => model.Code.Sequence, new { @id = "ifCodeSequence", @class = "CodeSequence sbm-input", @maxlength = 3 })
</div>
<div class="sbm-message"></div>
</div>
</div>
</div>
@* Description *@
<div class="sbm-form-control CodeDescription">
<div class="row">
<div class="col-sm-3">
<div class="sbm-label">
<label for="ifCodeDescription"><span>@Localiser["label.description"]</span></label>
</div>
</div>
<div class="col-sm-9">
<div class="sbm-control">
@Html.TextBoxFor(model => model.Code.Description, new { @id = "ifCodeDescription", @class = "CodeDescription sbm-input", @maxlength = @Model.GetControlMaxLength(SaCodeTypeDbDfn.ColumnIdx.SaCode_Description) })
</div>
<div class="sbm-message"></div>
</div>
</div>
</div>
@* Status *@
<div class="sbm-form-control CodeStatus">
<div class="row">
<div class="col-sm-3">
<div class="sbm-label">
<label for="cbCodeStatus"><span>@Localiser["label.status"]</span><span class="required">&nbsp;*</span></label>
</div>
</div>
<div class="col-sm-9">
<div class="sbm-control">
<select id="cbCodeStatus" class="sbm-select CodeStatus" name="Code.StatusCode"></select>
</div>
<div class="sbm-message"></div>
</div>
</div>
</div>
@* Notes *@
<div class="sbm-form-control CodeNotes">
<div class="row">
<div class="col-sm-3">
<div class="sbm-label">
<label for="ifCodeNotes"><span>@Localiser["label.notes"]</span></label>
</div>
</div>
<div class="col-sm">
<div class="sbm-control">
@Html.TextAreaFor(model => model.Code.Notes, new { @id = "ifCodeNotes", @class = "CodeNotes sbm-textarea", @rows = 3, @maxlength = @Model.GetControlMaxLength(SaCodeTypeDbDfn.ColumnIdx.SaCode_Notes) })
</div>
<div class="sbm-message"></div>
</div>
</div>
</div>
}
<div class="spacer2"></div>
</div>
@* Select Item dialog and script *@
<partial name="~/Views/Common/SelectItem.cshtml" />
<script>
sbm.data.codeTypes = [@foreach (var codeType in Model.CodeTypes)
{
<text>{code:'@(Localiser.GetJavascriptText(codeType.Code))',name:'@(Localiser.GetJavascriptText(codeType.Name))'},</text>
}];
</script>
</div>
@@ -0,0 +1,185 @@
@* Codes - Code Type *@
@model Model_CodeType
@inject SbmLocaliser Localiser
@{
ViewData["Title"] = "Code Types";
Model.AdditionalFiles_JS.Add("js/sbm-cod-typ.js");
}
<div class="sbm-panel CodeTypes fullheight">
<script type="text/javascript">
sbm.dlg.title.createItem = '@(Localiser.GetJavascriptText("caption.code.type.create"))';
sbm.dlg.title.updateItem = '@(Localiser.GetJavascriptText("caption.code.type.update"))';
</script>
<div class="sbm-panel-data sbm-flex">
@* Filter form - wrap header to include paging controls. *@
@using (@Html.BeginForm(FormMethod.Post, new { @id = "frmCodeTypeFilter", @class = "sbm-form", @onSubmit = "return false;" }))
{
<div class="sbm-panel-header">
<div class="row">
<div class="fixed">
<partial name="~/Views/Common/ResultsPaging.cshtml" />
</div>
<div class="col center"><div class="sbm-panel-caption">@Localiser["caption.code.types"]</div></div>
<div class="fixed">
@* Burger menu *@
<ul class="nav navbar-nav navbar-dark navbar-right sbm-burger">
<li class="dropdown-toggle caret-off">
<a href="#" class="dropdown" data-toggle="dropdown" role="button" aria-haspopup="true" aria-expanded="false"><span class="navbar-toggler-icon"></span></a>
<ul class="dropdown-menu dropdown-menu-right sbm-dropdown-menu">@* Menu items will be inserted here. *@</ul>
</li>
</ul>
</div>
</div>
</div>
@* Filter Panel *@
<div class="sbm-panel-filter">
<div class="row pb4">
<div class="col-sm-5 sbm-label text-left text-sm-right">@Localiser["label.search"]</div>
<div class="col-sm-7">
@Html.TextBoxFor(model => model.Filter.Search, new { @id = "", @class = "FilterSearch", @maxlength = "32" })
</div>
</div>
</div>
}
<div class="sbm-panel-item-table sbm-flex">
<table class="sbm-item-list">
<thead>
<tr>
<th class="pl-2">@Localiser["label.code"]</th>
<th class="">@Localiser["label.name"]</th>
<th class="center">@Localiser["label.codes"]</th>
<th class="center">@Localiser["label.status"]</th>
<th class="right" style="width:2%;"></th>
</tr>
<tr><td colspan="6"><div class="sbm-panel-messages"></div></td></tr>
</thead>
<tbody></tbody>
</table>
</div>
@* Element templates *@
<div class="sbm-panel-item-template">
@* Filter Burger option template - ul is ignored *@
<ul class="sbm-dropdown-menu CodeTypeFilter">
<li class="sbm-dropdown-item sbm-action"><div class="sbm-label">XXX</div></li>
</ul>
@* CodeType row template - table is ignored. *@
<table class="">
<tr class="sbm-row-item CodeType">
<td><div class="CodeTypeCode pl-1"></div></td>
<td><div class="CodeTypeName"></div></td>
<td class="center"><div class="CodeTypeCodes"></div></td>
<td><div class="CodeTypeStatusDesc center"></div></td>
<td>
<div class="right">
@* Burger menu *@
<ul class="nav navbar-nav navbar-light navbar-right sbm-burger">
<li class="dropdown-toggle caret-off">
<a href="#" class="dropdown" data-toggle="dropdown" role="button" aria-haspopup="true" aria-expanded="false"><span class="sbm-ellipsis-dot"></span><span class="sbm-ellipsis-dot"></span><span class="sbm-ellipsis-dot"></span></a>
<ul class="dropdown-menu dropdown-menu-right sbm-dropdown-menu">@* Menu items will be inserted here. *@</ul>
</li>
</ul>
</div>
</td>
</tr>
</table>
@* Item Burger option template - ul is ignored *@
<ul class="sbm-dropdown-menu CodeType">
<li class="sbm-dropdown-item sbm-action"><div class="sbm-label">XXX</div></li>
</ul>
</div>
@using (@Html.BeginForm(FormMethod.Post, new { @id = "frmCodeTypeId", @onSubmit = "return false;" }))
{
@Html.HiddenFor(model => model.CodeType.Id, new { @id = "", @class = "CodeTypeId" })
}
<div id="dlgCodeType" class="sbm-dialog">
<div class="spacer2"></div>
@using (@Html.BeginForm(FormMethod.Post, new { @id = "frmCodeType", @class = "sbm-form", @onSubmit = "return false;" }))
{
@Html.HiddenFor(model => model.CodeType.Exists, new { @id = "", @class = "CodeTypeExists" })
@Html.HiddenFor(model => model.CodeType.Id, new { @id = "", @class = "CodeTypeId" })
@* Code *@
<div class="sbm-form-control CodeTypeCode">
<div class="row">
<div class="col-sm-3">
<div class="sbm-label">
<label for="ifCodeTypeCode"><span>@Localiser["label.code"]</span><span class="required">&nbsp;*</span></label>
</div>
</div>
<div class="col-sm-9">
<div class="sbm-control">
@Html.TextBoxFor(model => model.CodeType.Code, new { @id = "ifCodeTypeCode", @class = "CodeTypeCode sbm-input", @maxlength = @Model.GetControlMaxLength(SaCodeTypeDbDfn.ColumnIdx.SaCodeType_Code) })
</div>
<div class="sbm-message"></div>
</div>
</div>
</div>
@* Name *@
<div class="sbm-form-control CodeTypeName">
<div class="row">
<div class="col-sm-3">
<div class="sbm-label">
<label for="ifCodeTypeName"><span>@Localiser["label.name"]</span><span class="required">&nbsp;*</span></label>
</div>
</div>
<div class="col-sm-9">
<div class="sbm-control">
@Html.TextBoxFor(model => model.CodeType.Name, new { @id = "ifCodeTypeName", @class = "CodeTypeName sbm-input", @maxlength = @Model.GetControlMaxLength(SaCodeTypeDbDfn.ColumnIdx.SaCodeType_Name) })
</div>
<div class="sbm-message"></div>
</div>
</div>
</div>
@* Status *@
<div class="sbm-form-control CodeTypeStatus">
<div class="row">
<div class="col-sm-3">
<div class="sbm-label">
<label for="cbCodeTypeStatus"><span>@Localiser["label.status"]</span><span class="required">&nbsp;*</span></label>
</div>
</div>
<div class="col-sm-9">
<div class="sbm-control">
<select id="cbCodeTypeStatus" class="sbm-select CodeTypeStatus" name="CodeType.StatusCode"></select>
</div>
<div class="sbm-message"></div>
</div>
</div>
</div>
@* Notes *@
<div class="sbm-form-control CodeTypeNotes">
<div class="row">
<div class="col-sm-3">
<div class="sbm-label">
<label for="ifCodeTypeNotes"><span>@Localiser["label.notes"]</span></label>
</div>
</div>
<div class="col-sm">
<div class="sbm-control">
@Html.TextAreaFor(model => model.CodeType.Notes, new { @id = "ifCodeTypeNotes", @class = "CodeTypeNotes sbm-textarea", @rows = 3, @maxlength = @Model.GetControlMaxLength(SaCodeTypeDbDfn.ColumnIdx.SaCodeType_Notes) })
</div>
<div class="sbm-message"></div>
</div>
</div>
</div>
}
<div class="spacer2"></div>
</div>
</div>
</div>
@@ -8,9 +8,6 @@
<div class="sbm-panel Commodities">
<script type="text/javascript">
sbm.lbl.refresh = '@(Localiser.GetJavascriptText("label.refresh"))';
sbm.lbl.create = '@(Localiser.GetJavascriptText("label.create"))';
sbm.lbl.edit = '@(Localiser.GetJavascriptText("label.edit"))';
</script>
<div class="row">
@@ -1,9 +1,12 @@
@model SbmModel_Portal
@inject SbmLocaliser Localiser
@{
Model.AdditionalFiles_JS.Add("js/sbm-resultsPaging.js");
}
<script src="~/js/sbm-resultsPaging.js?@Model.HrefVersion"></script>
<script type="text/javascript">
sbm.resultsPaging.msg.pageInfo = '@Localiser.GetJavascriptText("label.page.info","{0}","{1}")';
sbm.resultsPaging = sbm.resultsPaging || { fn: {}, msg: {}, contexts: [] };
sbm.resultsPaging.msg.pageInfo = '@Localiser.GetJavascriptText("label.page.info", "{0}", "{1}")';
</script>
@* Results paging *@
@@ -0,0 +1,53 @@
@model SbmModel_Portal
@inject SbmLocaliser Localiser
@{
// Ensure view is only included once in page.
if (!Model.RegisterView("Views/Common/SelectItem")) return;
// Scripts.
Model.AdditionalFiles_JS.Add("js/sbm-sel-itm.js");
}
<link rel="stylesheet" href="~/css/sbm-sel-itm.css?@Model.HrefVersion" />
<div id="dlgSelectItem" class="sbm-dialog sbm-form">
<div class="container-fluid h-100">
@* Selected Item *@
<div class="SelectedItem">
<div class="row">
<div class="col-sm-12"><div class="sbm-label">@Localiser["label.item.selected"]:</div></div>
<div class="col-sm-12"><div class="SelectedItemName"></div></div>
</div>
<div class="spacer2"></div>
</div>
@* Item Filter *@
<div class="ItemFilter sbm-form">
<div class="row">
<div class="col right"><div class="sbm-label">@Localiser["label.search"]</div></div>
<div class="fixed"><input type="search" id="ifItemFilter" class="sbm-input" autocomplete="off" onkeyup="sbm.data.selectItem.fn.displayItems();" /></div>
</div>
<div class="spacer2"></div>
</div>
@* Available items *@
<div class="row">
<div class="col"><div class="sbm-label bottom">@Localiser["label.items.available"]:</div></div>
</div>
<div class="row">
<div class="col-sm-12 sbm-selectItem-items AvailableItems"></div>
</div>
</div>
<div class="sbm-panel-item-template">
<div class="sbm-panel-item Item">
<div class="ItemName"></div>
</div>
</div>
</div>
@@ -8,9 +8,6 @@
<div class="sbm-panel Contacts">
<script type="text/javascript">
sbm.lbl.refresh = '@(Localiser.GetJavascriptText("label.refresh"))';
sbm.lbl.create = '@(Localiser.GetJavascriptText("label.create"))';
sbm.lbl.edit = '@(Localiser.GetJavascriptText("label.edit"))';
sbm.lbl.createOrg = '@(Localiser.GetJavascriptText("label.create.organisation"))';
sbm.lbl.createInd = '@(Localiser.GetJavascriptText("label.create.individual"))';
</script>
@@ -1,7 +1,108 @@
@{
ViewData["Title"] = "Home Page";
ViewData["Title"] = "Home Page";
bool showTestPage = false;
}
<div class="text-center">
<h1 class="display-4">Welcome</h1>
</div>
@if (!showTestPage)
{
<div class="text-center">
<h1 class="display-4">Welcome</h1>
</div>
}
else
{
<style>
.abc-panel-item-table
{
flex: 1;
width: 100%;
min-height: 0;
background-color: white;
overflow-y: auto;
}
table.abc-item-list
{
width: 100%;
border-spacing: 0;
border-collapse: separate;
overflow: auto;
}
table.abc-item-list thead tr
{
}
table.abc-item-list thead th
{
position: sticky;
top: 0;
padding: 4px 2px 2px 2px;
z-index: 1;
background-color: #f8f8f8;
border-bottom: 1px solid #d0d0d0;
}
table.abc-item-list tbody th
{
position: sticky;
left: 0;
background-color: #f8f8f8;
}
</style>
<div style="height:100%;background-color:lightcyan;">
<div style="display:flex;flex-direction:column;height:100%;padding:4px 8px;">
<div style="flex:0;padding:4px 8px;background-color:lightgreen;border:1px solid green;">Upper</div>
<div class="abc-panel-item-table" style="background-color:#f0e0e0;border:1px solid red;">
<table class="abc-item-list">
<thead>
<tr><th>Row</th></tr>
</thead>
<tbody>
<tr><td>1</td></tr>
<tr><td>2</td></tr>
<tr><td>3</td></tr>
<tr><td>4</td></tr>
<tr><td>5</td></tr>
<tr><td>6</td></tr>
<tr><td>7</td></tr>
<tr><td>8</td></tr>
<tr><td>9</td></tr>
<tr><td>10</td></tr>
<tr><td>11</td></tr>
<tr><td>12</td></tr>
<tr><td>13</td></tr>
<tr><td>14</td></tr>
<tr><td>15</td></tr>
<tr><td>16</td></tr>
<tr><td>17</td></tr>
<tr><td>18</td></tr>
<tr><td>19</td></tr>
<tr><td>20</td></tr>
<tr><td>1</td></tr>
<tr><td>2</td></tr>
<tr><td>3</td></tr>
<tr><td>4</td></tr>
<tr><td>5</td></tr>
<tr><td>6</td></tr>
<tr><td>7</td></tr>
<tr><td>8</td></tr>
<tr><td>9</td></tr>
<tr><td>10</td></tr>
<tr><td>11</td></tr>
<tr><td>12</td></tr>
<tr><td>13</td></tr>
<tr><td>14</td></tr>
<tr><td>15</td></tr>
<tr><td>16</td></tr>
<tr><td>17</td></tr>
<tr><td>18</td></tr>
<tr><td>19</td></tr>
<tr><td>20</td></tr>
</tbody>
</table>
</div>
<div style="flex:0;padding:4px 8px;background-color:lightblue;border:1px solid blue;">Lower</div>
</div>
</div>
}
@@ -54,26 +54,30 @@
}
@* Scripts *@
<script>
var sbm = sbm || { page: {}, url: {}, links: {}, data: {}, form: {}, ajax: {}, msg: {}, lbl: {}, dlg: { title: {}, btn: {} }, $frm: {}, $dlg: {}, $pnl: {} };
<script type="text/javascript">
var sbm = sbm || { page: {}, mdl: {}, fn: {}, url: {}, links: {}, data: {}, form: {}, ajax: {}, msg: {}, lbl: {}, dlg: { title: {}, btn: {} }, $frm: {}, $dlg: {}, $pnl: {}, session: {}, kli: {} };
sbm.url.base = '@Model.RedirectLocation.Base';
sbm.url.home = '@Model.RedirectLocation.Home';
sbm.url.sessionExpired = '@Model.RedirectLocation.Home_SessionExpired';
sbm.form.calendarDateFormat = '@(Model.CalendarDateFormat)';
sbm.lbl.refresh = '@(Localiser.GetJavascriptText("label.refresh"))';
sbm.lbl.create = '@(Localiser.GetJavascriptText("label.create"))';
sbm.lbl.edit = '@(Localiser.GetJavascriptText("label.edit"))';
sbm.lbl.calendar = '@Localiser.GetJavascriptText("label.calendar")';
sbm.dlg.title.alert = '@Localiser.GetJavascriptText("dialog.title.alert")';
sbm.dlg.title.confirm = '@Localiser.GetJavascriptText("dialog.title.confirm")';
sbm.dlg.title.progress = '@Localiser.GetJavascriptText("dialog.title.progress")';
sbm.dlg.title.signOut = '@Localiser.GetJavascriptText("label.signout")';
sbm.dlg.btn.yes = '@Localiser["label.yes"]';
sbm.dlg.btn.no = '@Localiser["label.no"]';
sbm.dlg.btn.ok = '@Localiser["label.ok"]';
sbm.dlg.btn.close = '@Localiser["label.close"]';
sbm.dlg.btn.cancel = '@Localiser["label.cancel"]';
sbm.lbl.yes = '@Localiser["label.yes"]';
sbm.lbl.no = '@Localiser["label.no"]';
sbm.lbl.ok = '@Localiser["label.ok"]';
sbm.lbl.close = '@Localiser["label.close"]';
sbm.lbl.save = '@Localiser["label.save"]';
sbm.lbl.cancel = '@Localiser["label.cancel"]';
sbm.lbl.availableCharacter = '@Localiser.GetJavascriptText("label.characters.one")';
sbm.lbl.availableCharacters = '@Localiser.GetJavascriptText("label.characters.count", "{0}")';
sbm.lbl.noneSelected = '@Localiser.GetJavascriptText("label.selected.none")';
sbm.lbl.numberSelected = '@Localiser.GetJavascriptText("label.selected.count", "{0}")';
sbm.dlg.title.alert = '@Localiser.GetJavascriptText("dialog.title.alert")';
sbm.dlg.title.confirm = '@Localiser.GetJavascriptText("dialog.title.confirm")';
sbm.dlg.title.progress = '@Localiser.GetJavascriptText("dialog.title.progress")';
sbm.dlg.title.signOut = '@Localiser.GetJavascriptText("label.signout")';
sbm.msg.itemRemoveConfirm = '@Localiser.GetJavascriptText("msg.confirm.item.remove")';
sbm.msg.signOutConfirm = '@Localiser.GetJavascriptText("security.msg.signout.confirm")';
sbm.msg.somethingGoneWrong = '@Localiser.GetJavascriptText("msg.something.gone.wrong")';
@@ -117,14 +121,34 @@
{
<li class="nav-item dropdown">
<a class="nav-link text-dark dropdown-toggle" href="#" id="navbarDropdown" role="button" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
@Localiser["label.reference"]
@Localiser["label.contacts"]
</a>
<div class="dropdown-menu" aria-labelledby="navbarDropdown">
<div class="dropdown-item text-dark sbm-action" onclick="sbm.goToPage('Contact');">@Localiser["label.contacts"]</div>
<div class="dropdown-item text-dark sbm-action" onclick="sbm.goToPage('Commodity');">@Localiser["label.commodities"]</div>
<div class="dropdown-item text-dark sbm-action" onclick="sbm.goToPage('Association');">@Localiser["label.associations"]</div>
</div>
</li>
<li class="nav-item dropdown">
<a class="nav-link text-dark dropdown-toggle" href="#" id="navbarDropdown" role="button" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
@Localiser["label.commodities"]
</a>
<div class="dropdown-menu" aria-labelledby="navbarDropdown">
<div class="dropdown-item text-dark sbm-action" onclick="sbm.goToPage('Commodity');">@Localiser["label.commodities"]</div>
<div class="dropdown-item text-dark sbm-action" onclick="sbm.goToPage('Assets/AssetCategory');">@Localiser["caption.asset.categories"]</div>
<div class="dropdown-item text-dark sbm-action" onclick="sbm.goToPage('Assets/Asset');">@Localiser["label.assets"]</div>
<div class="dropdown-item text-dark sbm-action" onclick="sbm.goToPage('Assets/VehicleClass');">@Localiser["caption.vehicle.classes"]</div>
<div class="dropdown-item text-dark sbm-action" onclick="sbm.goToPage('Assets/Vehicle');">@Localiser["label.vehicles"]</div>
</div>
</li>
<li class="nav-item dropdown">
<a class="nav-link text-dark dropdown-toggle" href="#" id="navbarDropdown" role="button" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
@Localiser["label.reference"]
</a>
<div class="dropdown-menu" aria-labelledby="navbarDropdown">
<div class="dropdown-item text-dark sbm-action" onclick="sbm.goToPage('Codes/CodeType');">@Localiser["caption.code.types"]</div>
<div class="dropdown-item text-dark sbm-action" onclick="sbm.goToPage('Codes/Code');">@Localiser["label.codes"]</div>
</div>
</li>
<li class="nav-item dropdown">
<a class="nav-link text-dark dropdown-toggle" href="#" id="navbarDropdown" role="button" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
@Localiser["label.security"]
@@ -144,7 +168,7 @@
@* Page body *@
<div id="sbm-page-body" class="container">
<main role="main" class="pb-3">
<main role="main" class="pb-3 fullheight">
@RenderBody()
</main>
</div>
@@ -1,4 +1,8 @@
@using Sarand.BusinessMgr.Net.Core.Web.App
@using Sarand.BusinessMgr.Net.Standard.Base.App
@using Sarand.BusinessMgr.Net.Core.Web.Portal.Models
@using Sarand.BusinessMgr.Web.Portal.Models
@using Sarand.BusinessMgr.Web.Portal.Models.Commodities
@using Sarand.BusinessMgr.Web.Portal.Models.Codes
@using Sarand.BusinessMgr.Web.Portal.Models.Assets
@using Sarand.Net.Standard.Base.CodeTypes
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers
@@ -4,7 +4,7 @@
"Environments": [ "Development" ],
"Instance": "Dev",
"ControlDatabase": {
"Location": "MsSql:Roman\\SQL2016.SaBusinessMgr_Control",
"Location": "MsSql:.\\SQL2016.SaBusinessMgr_Control",
"User": "sapphire",
"Password": "sarand"
},
@@ -26,6 +26,18 @@
"Location": "MsSql:Roman\\SQL2016.SaBusinessMgr_BPE",
"User": "sapphire",
"Password": "sarand"
},
{
"Identifiers": [ "MIMO" ],
"Location": "MsSql:.\\SQL2016.SaBusinessMgr_Mimo",
"User": "sapphire",
"Password": "sarand"
},
{
"Identifiers": [ "Firtree-X" ],
"Location": "MsSql:Sarand110\\SQL2016.SaBusinessMgr_Firtree",
"User": "firtree",
"Password": "sarand"
}
],
"Email": {
@@ -200,6 +200,11 @@ td.action
width: 100%;
}
.fullheight
{
height: 100%;
}
.inlineblock, .inlineBlock, .iblock, .inlineb, .ib
{
display: inline-block;
@@ -2,75 +2,111 @@
BusMgr Portal - Data stylesheet.
*/
/* Filter panel */
.sbm-panel-filter
:root
{
margin: 8px auto;
--color-OliveGreen: #808000;
--color-DataPanel-main: var(--color-OliveGreen);
}
/* Data panel */
.sbm-panel-data
{
margin: 6px auto;
background-color: #f8f8f8;
border: solid 1px #979797;
border: solid 1px var(--color-DataPanel-main);
border-radius: 6px;
overflow: hidden;
}
@media (min-width: 576px)
.sbm-layout-flex .sbm-panel-data.sbm-flex
{
.sbm-panel-filter
{
}
height: 100%;
display: flex;
flex-direction: column;
}
.sbm-panel-filter .sbm-header
/* Data panel - Header */
.sbm-panel-data .sbm-panel-header
{
padding: 1px 8px 2px 8px;
min-height: 24px;
color: #f0f0f0;
background-color: #979797;
border-bottom: solid 1px #979797;
background-color: var(--color-DataPanel-main);
border-bottom: solid 1px var(--color-DataPanel-main);
}
.sbm-panel-filter .sbm-header .sbm-caption
.sbm-panel-data .sbm-panel-header .sbm-panel-caption
{
margin-top: 4px;
font-size: 1.2rem;
font-weight: bold;
}
.sbm-panel-filter .sbm-header .sbm-action
/* Data panel - Actions */
.sbm-panel-data ul.nav.sbm-burger
{
margin: 2px 4px 0 8px;
margin-top: 2px;
}
.sbm-panel-data .sbm-panel-header .sbm-action
{
margin: 2px 0 0 8px;
padding: 2px 4px;
border-radius: 3px;
}
.sbm-panel-filter .sbm-header .sbm-action:hover
.sbm-panel-data .sbm-panel-header .sbm-action:hover
{
background-color: #e0e0e0;
/*box-shadow: 0 0 4px 2px #80808080;*/
}
.sbm-panel-filter .sbm-content
/* Data panel - Filter */
.sbm-panel-data .sbm-panel-filter
{
margin: 4px 8px;
padding: 4px 8px;
padding: 6px 8px;
border-bottom: solid 1px var(--color-DataPanel-main);
}
.sbm-panel-data .sbm-panel-filter .sbm-label
{
padding: 2px 8px 0 0;
}
/* Data panel - Content */
.sbm-panel-data .sbm-content
{
margin: 4px 4px;
padding: 4px 4px;
background-color: #f8f8f8;
}
.sbm-panel-filter .sbm-content .sbm-label
.sbm-panel-data .sbm-content .sbm-label
{
padding-top: 2px;
padding-top: 6px;
padding-right: 12px;
text-align: right;
}
.sbm-panel-filter .sbm-content input[type="text"],
.sbm-panel-filter .sbm-content select
.sbm-panel-data .sbm-panel-content .sbm-value
{
padding-top: 2px;
}
.sbm-panel-data .sbm-content input[type="text"],
.sbm-panel-data .sbm-content select
{
min-width: 75%;
max-width: 90%;
width: 90%;
}
.sbm-panel-filter .sbm-content .sbm-button
.sbm-panel-data .sbm-content .sbm-button
{
min-width: auto;
margin-left: 4px;
@@ -89,41 +125,281 @@
font-size: .67rem;
}
.sbm-panel-filter .sbm-header .sbm-results-paging .sbm-action
.sbm-panel-data .sbm-panel-header .sbm-results-paging .sbm-action
{
margin: 2px 1px 0 1px;
background-color: #b0b0b0;
box-shadow: 0 0 0 1px #40404080 inset;
}
.sbm-panel-filter .sbm-header .sbm-results-paging .sbm-action:hover
.sbm-panel-data .sbm-panel-header .sbm-results-paging .sbm-action:hover
{
background-color: #c0c0c0;
}
/* Panel header - Burger */
ul.nav.sbm-burger
{
margin: 0;
}
ul.nav.sbm-burger .dropdown
{
margin: 2px 0 2px 0;
padding: 2px 2px;
border: 1px solid transparent;
border-radius: 2px;
}
ul.nav.sbm-burger .dropdown:hover
{
opacity: .75;
}
ul.nav.sbm-burger.navbar-light .dropdown:hover
{
background-color: #f8f8f8;
border-color: #606060;
}
ul.nav.sbm-burger.navbar-dark .dropdown:hover
{
border-color: #c0c0c0;
}
ul.nav.sbm-burger .sbm-ellipsis-dot
{
display: inline-block;
width: 4px;
height: 4px;
margin: 0 1px 3px 1px;
background-color: #808080;
border-radius: 2px;
}
ul.nav.sbm-burger ul.sbm-dropdown-menu
{
padding: 4px 8px 4px 4px;
border-radius: 2px 2px 4px 8px;
box-shadow: 0 0 8px 2px #80808080;
}
ul.nav.sbm-burger ul.sbm-dropdown-menu li.sbm-action.sbm-dropdown-item
{
margin: 2px 4px 2px 0;
padding: 2px 4px 2px 30px;
background-repeat: no-repeat;
background-position: left center;
}
ul.nav.sbm-burger ul.sbm-dropdown-menu li.sbm-action.sbm-dropdown-item.sbm-refresh
{
background-image: url('../img/action/refresh24.png');
}
ul.nav.sbm-burger ul.sbm-dropdown-menu li.sbm-action.sbm-dropdown-item.sbm-info
{
background-image: url('../img/action/info24.png');
}
ul.nav.sbm-burger ul.sbm-dropdown-menu li.sbm-action.sbm-dropdown-item.sbm-add
{
background-image: url('../img/action/add24.png');
}
ul.nav.sbm-burger ul.sbm-dropdown-menu li.sbm-action.sbm-dropdown-item.sbm-edit
{
background-image: url('../img/action/edit24.png');
}
ul.nav.sbm-burger ul.sbm-dropdown-menu li.sbm-action.sbm-dropdown-item.sbm-remove
{
background-image: url('../img/action/delete24.png');
}
ul.nav.sbm-burger ul.sbm-dropdown-menu li.sbm-action.sbm-dropdown-item.sbm-experience
{
background-image: url('../img/action/trophy24.png');
}
ul.nav.sbm-burger ul.sbm-dropdown-menu li.sbm-action.sbm-dropdown-item.sbm-user
{
background-image: url('../img/action/user24.png');
}
ul.nav.sbm-burger ul.sbm-dropdown-menu li.sbm-action.sbm-dropdown-item.sbm-users
{
background-image: url('../img/action/users24.png');
}
ul.nav.sbm-burger ul.sbm-dropdown-menu li.sbm-action.sbm-dropdown-item.sbm-mail
{
background-image: url('../img/action/mail24.png');
}
ul.nav.sbm-burger ul.sbm-dropdown-menu li.sbm-action.sbm-dropdown-item.sbm-location
{
background-image: url('../img/action/location-24.png');
}
ul.nav.sbm-burger ul.sbm-dropdown-menu li.sbm-action.sbm-dropdown-item.sbm-image
{
background-image: url('../img/action/image-24.png');
}
ul.nav.sbm-burger ul.sbm-dropdown-menu li.sbm-action.sbm-dropdown-item.sbm-process
{
background-image: url('../img/action/process24.png');
}
ul.nav.sbm-burger ul.sbm-dropdown-menu li.sbm-action.sbm-dropdown-item.sbm-upload
{
background-image: url('../img/action/upload24.png');
}
ul.nav.sbm-burger ul.sbm-dropdown-menu li.sbm-action.sbm-dropdown-item.sbm-download
{
background-image: url('../img/action/download24.png');
}
ul.nav.sbm-burger ul.sbm-dropdown-menu li.sbm-action.sbm-dropdown-item.sbm-network
{
background-image: url('../img/action/network-24.png');
}
ul.nav.sbm-burger ul.sbm-dropdown-menu li.sbm-action.sbm-dropdown-item.sbm-close
{
background-image: url('../img/action/prev24.png');
}
ul.nav.sbm-burger ul.sbm-dropdown-menu li.sbm-action.sbm-dropdown-itemlock
{
background-image: url('../img/action/lock24.png');
}
ul.nav.sbm-burger ul.sbm-dropdown-menu li.sbm-action.sbm-dropdown-item.sbm-unlock
{
background-image: url('../img/action/unlock24.png');
}
ul.nav.sbm-burger ul.sbm-dropdown-menu li.sbm-action.sbm-dropdown-item:hover
{
background-color: #f8f8f8;
box-shadow: none;
}
ul.nav.sbm-burger ul.sbm-dropdown-menu li.sbm-action.sbm-dropdown-item .sbm-label
{
margin: 0;
padding: 0 0 0 4px;
}
ul.nav.sbm-burger ul.sbm-dropdown-menu li.sbm-action.sbm-dropdown-item:hover .sbm-label
{
background-color: #f8f8f8;
box-shadow: 0 0 2px 1px #80808080;
}
/* Data Items panel */
.sbm-panel-results
{
}
@media (min-width: 576px)
{
.sbm-panel-results
{
}
}
/* Messages */
.sbm-panel-results .sbm-panel-messages .sbm-message
.sbm-panel-messages
{
margin-top: 8px;
padding-left: 24px;
margin: 8px 16px;
padding: 2px 12px 2px 8px;
font-size: 16px;
}
.sbm-panel-messages .sbm-message
{
margin: 4px 0;
padding-left: 24px;
background-position: left top;
background-repeat: no-repeat;
}
.sbm-panel-results .sbm-panel-messages .sbm-message.sbm-error
.sbm-panel-messages .sbm-message.sbm-error
{
color: #e3120b;
background-image: url("../img/icon/error-18.png");
}
/* Item List table */
.sbm-panel-item-table
{
width: 100%;
min-height: 240px;
overflow-y: auto;
}
.sbm-layout-flow .sbm-panel-item-table
{
}
.sbm-layout-flex .sbm-panel-item-table.sbm-flex
{
flex: 1;
min-height: 0;
}
table.sbm-item-list
{
width: 100%;
border-spacing: 0;
border-collapse: separate;
overflow: auto;
}
table.sbm-item-list thead tr
{
}
table.sbm-item-list thead th
{
position: sticky;
top: 0;
padding: 4px 2px 2px 2px;
z-index: 1;
background-color: #f8f8f8;
border-bottom: 1px solid #d0d0d0;
}
table.sbm-item-list tbody th
{
position: sticky;
left: 0;
background-color: #f8f8f8;
}
table.sbm-item-list tbody tr:hover
{
background-color: lightyellow;
}
table.sbm-item-list tbody tr.sbm-sel
{
background-color: palegreen;
}
table.sbm-item-list tbody td
{
padding: 0 2px;
}
/* Item */
.sbm-panel-item-template
@@ -189,13 +465,6 @@
font-size: 0.8rem;
}
@media (min-width: 576px)
{
.sbm-panel-filter
{
}
}
/* Item statuses */
.sbm-status-awa
@@ -203,7 +472,7 @@
color: darkorange;
}
.sbm-status-atv
.sbm-status-atv, .sbm-status-cur
{
color: green;
}
@@ -178,15 +178,17 @@
overflow-y: scroll;
}
.sbm-form .sbm-input:read-only, .sbm-form .sbm-textarea:read-only
.sbm-form .sbm-input:read-only, .sbm-form .sbm-textarea:read-only, .sbm-form .sbm-select:disabled
{
background-color: #f8f8f8;
color: #404040;
opacity: 1;
background-color: #f0f0f0;
}
.sbm-form .sbm-form-control.sbm-error .sbm-input, .sbm-form .sbm-form-control.sbm-error .sbm-textarea, .sbm-form .sbm-form-control.sbm-error .sbm-select
{
color: #DC143C;
border-color: #DC143C;
color: #606060; /*#DC143C;*/
border-color: #606060; /*#DC143C;*/
}
/* Control messages */
@@ -203,6 +205,7 @@
.sbm-form .sbm-form-control.sbm-error .sbm-message
{
min-height: 18px;
display: block;
color: #e3120b;
background-image: url("../img/icon/error-18.png");
@@ -28,7 +28,7 @@ html
{
html
{
font-size: 16px;
font-size: 14px;
}
}
@@ -37,7 +37,38 @@ body
font-family: Arial, Verdana, Helvetica, Lucida, sans-serif;
}
body > .container
@media screen and (max-height: 399px)
{
#sbm-page-body
{
min-height: 240px;
}
}
@media screen and (min-height: 400px)
{
html
{
height: 100vh;
}
body.sbm-layout-flex
{
height: 100vh;
display: flex;
flex-direction: column;
}
body.sbm-layout-flex #sbm-page-body
{
flex: 1;
min-height:0;
}
}
/* Responsive */
body > .container, header > .container, footer > .container
{
padding: 0 0.5%;
}
@@ -66,32 +97,6 @@ body > .container
}
}
/* Sticky footer styles */
html
{
position: relative;
min-height: 100%;
}
body
{
/* Margin bottom by footer height */
margin-bottom: 60px;
}
.footer
{
position: absolute;
bottom: 0;
width: 100%;
padding: 0 2%;
white-space: nowrap;
/* Set the fixed height of the footer here */
height: 60px;
line-height: 60px; /* Vertically center the text there */
}
/* Header */
#sbm-page-header
@@ -126,8 +131,20 @@ button.accept-policy
line-height: inherit;
}
/* Information */
.sbm-para
{
margin: 8px 0;
}
/* Footer */
#sbm-page-footer
{
margin: 4px 0;
}
#sbm-page-footer, #sbm-page-footer div
{
font-size: .8rem;
@@ -0,0 +1,61 @@
/*
Item Selection stylesheet.
*/
/* Panels */
#dlgSelectItem .SelectedItemName
{
min-height: 1.5rem;
padding: 2px 4px;
background-color: #f0f0f0;
border: 1px solid #b0b0b0;
border-radius: 4px;
overflow: hidden;
}
#dlgSelectItem .ItemFilter .sbm-label
{
margin-right: 6px;
}
#dlgSelectItem .sbm-button
{
padding: 2px 8px;
border-radius: 6px;
}
#dlgSelectItem .sbm-selectItem-items
{
margin-bottom: 8px;
padding: 2px 0;
background-color: #f0f0f0;
border: 1px solid #b0b0b0;
border-top-left-radius: 6px;
border-bottom-left-radius: 6px;
overflow-x: hidden;
overflow-y: scroll;
}
#dlgSelectItem.sbm-form button.sbm-button
{
margin: 4px 0;
}
#dlgSelectItem .sbm-panel-item.Item
{
margin: 2px 4px;
padding: 2px 4px;
background-color: #fff;
border: 2px solid #d8d8ff;
border-radius: 4px;
cursor: pointer;
}
#dlgSelectItem .sbm-panel-item.Item.sel
{
background-color: #ececff;
border-color: #8a8aff;
}
/* The End */
@@ -0,0 +1,206 @@
/*
* SBM - Portal - Assets - Asset Category.
*/
(function (win, doc, undef)
{
"use strict";
let ac = sbm.AssetCat = { data: {}, ajax: {} };
let $pnl = ac.$pnl = {}, $frm = ac.$frm = {}, $dlg = ac.$dlg = {};
//--------------------------------------------------------------
ac.refreshResults = function (delay)
{
if (!ac.awaitingRefreshResponse)
{
$('.sbm-burger', $pnl.header).invisible();
$pnl.table.find('tbody tr').remove();
$pnl.msgs.hide().empty().append($('<div class="sbm-message">' + sbm.msg.dataQuerying + '</div>')).fadeIn(200);
}
ac.ajax.assetCategories = ac.ajax.assetCategories || { url: sbm.url.base + 'Assets/AssetCategory/Ajax/GetAssetCategories', formId: $frm.filter.prop('id'), callbackFn: ac.handleAssetCategories };
sbm.doAjaxRequest(ac.ajax.assetCategories, delay || 50);
ac.awaitingRefreshResponse = true;
}
ac.handleAssetCategories = function (response)
{
//sbm.debug('handleAssetCategories', response);
delete ac.awaitingRefreshResponse;
let $table = $('table', $pnl.table).find('tbody tr').remove().end();
$pnl.msgs.hide().empty();
sbm.refreshPaging(response, '#frmAssetCategoryFilter', ac.refreshResults);
if (!response.success) return;
// Reference data - Statuses.
if (response.statuses) sbm.data.statuses = response.statuses;
if (response.statusesCreate) sbm.data.statusesCreate = response.statusesCreate;
if (response.statusCodeDefault) sbm.data.statusCodeDefault = response.statusCodeDefault;
// Menu.
var $bgr = $('.sbm-burger', $pnl.header).invisible();
var $mnu = $('ul.sbm-dropdown-menu', $bgr).empty();
$mnu.append(ac.getFilterMenuOption(sbm.lbl.refresh, function () { ac.refreshResults(); }, 'sbm-refresh'));
$mnu.append(ac.getFilterMenuOption(sbm.lbl.create, function () { ac.createItem(); }, 'sbm-add'));
$bgr.toggleVisibility(!!$('.sbm-dropdown-item', $mnu).length);
// Items.
var items = response.assetCategories || [];
if (!items.length)
return $pnl.msgs.append($('<div class="sbm-message">' + sbm.msg.dataNoneFound + '</div>')).fadeIn(500);
for (let itemIdx = 0; itemIdx < items.length; itemIdx++)
$table.append(ac.populateItem(ac.getItemRowTemplate(), items[itemIdx]));
}
ac.populateItem = function ($item, item)
{
if (!item) return;
$pnl.msgs.hide().empty();
$item.prop('id', ac.getItemId(item));
$('.AssetCategoryType', $item).text(item.typeDesc);
$('.AssetCategoryCode', $item).text(item.code);
$('.AssetCategoryDescription', $item).text(item.descBrief);
$('.AssetCategoryStatusDesc', $item).text(item.statusDesc).removeClass(function (idx, name) { return (name.match(/(^|\s)sbm-status-\S+/g) || []).join(' '); }).addClass('sbm-status-' + item.statusCode.toLowerCase());
// Actions.
let fnUpd = function () { ac.updateItem($item, item) };
var $bgr = $('.sbm-burger', $item).invisible();
let $mnu = $('ul.sbm-dropdown-menu', $bgr).empty();
if (!item.deleted)
{
$mnu.append(ac.getItemMenuOption(sbm.lbl.edit, fnUpd, 'sbm-edit'));
$('.AssetCategoryCode', $item).off('click').on('click', fnUpd).addClass('sbm-action-text');
$item.off('dblclick').on('dblclick', fnUpd);
}
$bgr.toggleVisibility(!!$('.sbm-dropdown-item', $mnu).length);
return $item;
}
ac.getItemId = function (item)
{
return 'Itm_' + (item || {}).id;
}
//--------------------------------------------------------------
ac.getFilterDropdownTemplate = function ()
{
return $('.sbm-dropdown-menu.AssetCategoryFilter li.sbm-dropdown-item', $pnl.tmpls).clone();
}
ac.getItemRowTemplate = function ()
{
return $('table tr.sbm-row-item.AssetCategory', $pnl.tmpls).clone();
}
ac.getItemDropdownTemplate = function ()
{
return $('.sbm-dropdown-menu.AssetCategory li.sbm-dropdown-item', $pnl.tmpls).clone();
}
ac.getFilterMenuOption = function (lbl, fn, cls)
{
return ac.getMenuOption(ac.getFilterDropdownTemplate(), lbl, fn, cls);
}
ac.getItemMenuOption = function (lbl, fn, cls)
{
return ac.getMenuOption(ac.getItemDropdownTemplate(), lbl, fn, cls);
}
ac.getMenuOption = function ($opt, lbl, fn, cls)
{
return $opt.find('div.sbm-label').text(lbl).end().on('click', fn).addClass(cls);
}
//--------------------------------------------------------------
ac.createItem = function ()
{
ac.clearItem();
sbm.populateSelectCodeDesc('#cbAssetCategoryStatus', sbm.data.statusesCreate);
$('#cbAssetCategoryStatus').val(sbm.data.statusCodeDefault).prop('readonly', false);
$dlg.item.dialog('option', 'title', sbm.dlg.title.createItem);
ac.openAssetCategoryDlg();
}
ac.openAssetCategoryDlg = function ()
{
sbm.openDialog($dlg.item, 600, 600);
}
ac.clearItem = function ()
{
let frm = $frm.item;
$('.AssetCategoryExists,.AssetCategoryId,input[type="text"],textarea,select', frm).val('');
sbm.populateSelectCodeDesc('#cbAssetCategoryStatus', sbm.data.statuses);
$('.AssetCategoryType,.AssetCategoryStatus', frm).prop('disabled', false);
sbm.clearControlErrors();
}
//--------------------------------------------------------------
ac.updateItem = function ($item, item)
{
ac.clearItem();
$('.AssetCategoryId', $frm.itemId).val(item.id);
sbm.ajax.updateItem = sbm.ajax.updateItem || { url: sbm.url.base + 'Assets/AssetCategory/Ajax/GetItem', formId: $frm.itemId.prop('id'), callbackFn: ac.onUpdateItem };
sbm.doAjaxRequest(sbm.ajax.updateItem, 50);
}
ac.onUpdateItem = function (response)
{
//sbm.debug('onUpdateItem', response);
let frm = $frm.item;
ac.clearItem();
if (!response.success) return;
let item = response.assetCategory;
$('.AssetCategoryExists', frm).val(1);
$('.AssetCategoryId', frm).val(item.id);
$('.AssetCategoryType', frm).val(item.typeCode).prop('disabled', true);
$('.AssetCategoryCode', frm).val(item.code);
$('.AssetCategoryDescription', frm).val(item.descBrief);
$('.AssetCategoryNotes', frm).val(item.notes);
$('.AssetCategoryStatus', frm).val(item.statusCode).prop('disabled', true);
sbm.clearControlErrors();
$dlg.item.dialog('option', 'title', sbm.dlg.title.updateItem);
ac.openAssetCategoryDlg();
}
//--------------------------------------------------------------
ac.storeItem = function ()
{
sbm.ajax.storeItem = sbm.ajax.storeItem || { url: sbm.url.base + 'Assets/AssetCategory/Ajax/StoreItem', formId: 'frmAssetCategory', callbackFn: ac.onStoreItem };
sbm.doAjaxRequest(sbm.ajax.storeItem, 50);
}
ac.onStoreItem = function (response)
{
//sbm.debug('onStoreItem', response);
sbm.refreshControlValues(response.controlValues);
if (!response.success) return;
$dlg.item.dialog('close');
var item = response.assetCategory;
var $item = $('#' + ac.getItemId(item));
if (!$item.length)
{
$item = ac.getItemRowTemplate();
$('table tbody', $pnl.table).prepend($item).show();
}
ac.populateItem($item, item);
// Force repopulation of sprts.
delete ac.data.assetCategories;
}
//--------------------------------------------------------------
$(function ()
{
$pnl.items = $('.sbm-panel.AssetCategories');
$pnl.header = $('.sbm-panel-header', $pnl.items);
$pnl.filter = $('.sbm-panel-filter', $pnl.items);
$pnl.msgs = $('.sbm-panel-messages', $pnl.items);
$pnl.table = $('.sbm-panel-item-table', $pnl.items);
$pnl.tmpls = $('.sbm-panel-item-template', $pnl.items);
$frm.filter = $('#frmAssetCategoryFilter');
$('.FilterSearch', $frm.filter).on('input', function () { ac.refreshResults(250); });
$frm.itemId = $('#frmAssetCategoryId');
var opt = sbm.dlgOpts();
opt.buttons[sbm.lbl.save] = function () { ac.storeItem(); };
opt.buttons[sbm.lbl.cancel] = function () { $(this).dialog('close'); };
$dlg.item = $('#dlgAssetCategory').dialog(opt);
$frm.item = $('#frmAssetCategory');
$($frm.item).on('input', '.sbm-input, .sbm-textarea', function () { sbm.clearControlError(this); }).on('change', '.sbm-select', function () { sbm.clearControlError(this); });
setTimeout(ac.refreshResults, 250);
});
//--------------------------------------------------------------
}(window, window.document));
/* The End */
@@ -0,0 +1,230 @@
/*
* SBM - Portal - Assets - Asset.
*/
(function (win, doc, undef)
{
"use strict";
let ac = sbm.AssetCat = { data: {}, ajax: {} };
let $pnl = ac.$pnl = {}, $frm = ac.$frm = {}, $dlg = ac.$dlg = {};
//--------------------------------------------------------------
ac.refreshResults = function (delay)
{
if (!ac.awaitingRefreshResponse)
{
$('.sbm-burger', $pnl.header).invisible();
$pnl.table.find('tbody tr').remove();
$pnl.msgs.hide().empty().append($('<div class="sbm-message">' + sbm.msg.dataQuerying + '</div>')).fadeIn(200);
}
ac.ajax.assets = ac.ajax.assets || { url: sbm.url.base + 'Assets/Asset/Ajax/GetAssets', formId: $frm.filter.prop('id'), callbackFn: ac.handleAssets };
sbm.doAjaxRequest(ac.ajax.assets, delay || 50);
ac.awaitingRefreshResponse = true;
}
ac.handleAssets = function (response)
{
sbm.debug('handleAssets', response);
delete ac.awaitingRefreshResponse;
let $table = $('table', $pnl.table).find('tbody tr').remove().end();
$pnl.msgs.hide().empty();
sbm.refreshPaging(response, '#frmAssetFilter', ac.refreshResults);
if (!response.success) return;
// Reference data - Statuses.
if (response.statuses) sbm.data.statuses = response.statuses;
if (response.statusesCreate) sbm.data.statusesCreate = response.statusesCreate;
if (response.statusCodeDefault) sbm.data.statusCodeDefault = response.statusCodeDefault;
// Menu.
var $bgr = $('.sbm-burger', $pnl.header).invisible();
var $mnu = $('ul.sbm-dropdown-menu', $bgr).empty();
$mnu.append(ac.getFilterMenuOption(sbm.lbl.refresh, function () { ac.refreshResults(); }, 'sbm-refresh'));
$mnu.append(ac.getFilterMenuOption(sbm.lbl.create, function () { ac.createItem(); }, 'sbm-add'));
$bgr.toggleVisibility(!!$('.sbm-dropdown-item', $mnu).length);
// Items.
var items = response.assets || [];
if (!items.length)
return $pnl.msgs.append($('<div class="sbm-message">' + sbm.msg.dataNoneFound + '</div>')).fadeIn(500);
for (let itemIdx = 0; itemIdx < items.length; itemIdx++)
$table.append(ac.populateItem(ac.getItemRowTemplate(), items[itemIdx]));
}
ac.populateItem = function ($item, item)
{
if (!item) return;
$pnl.msgs.hide().empty();
$item.prop('id', ac.getItemId(item));
$('.AssetCategory', $item).text(item.categoryDesc);
$('.AssetSerial', $item).text(item.serialNumber);
$('.AssetDescription', $item).text(item.descBrief);
$('.AssetStatusDesc', $item).text(item.statusDesc).removeClass(function (idx, name) { return (name.match(/(^|\s)sbm-status-\S+/g) || []).join(' '); }).addClass('sbm-status-' + item.statusCode.toLowerCase());
// Actions.
let fnUpd = function () { ac.updateItem($item, item) };
var $bgr = $('.sbm-burger', $item).invisible();
let $mnu = $('ul.sbm-dropdown-menu', $bgr).empty();
if (!item.deleted)
{
$mnu.append(ac.getItemMenuOption(sbm.lbl.edit, fnUpd, 'sbm-edit'));
$('.AssetSerial', $item).off('click').on('click', fnUpd).addClass('sbm-action-text');
$item.off('dblclick').on('dblclick', fnUpd);
}
$bgr.toggleVisibility(!!$('.sbm-dropdown-item', $mnu).length);
return $item;
}
ac.getItemId = function (item)
{
return 'Itm_' + (item || {}).id;
}
//--------------------------------------------------------------
ac.getFilterDropdownTemplate = function ()
{
return $('.sbm-dropdown-menu.AssetFilter li.sbm-dropdown-item', $pnl.tmpls).clone();
}
ac.getItemRowTemplate = function ()
{
return $('table tr.sbm-row-item.Asset', $pnl.tmpls).clone();
}
ac.getItemDropdownTemplate = function ()
{
return $('.sbm-dropdown-menu.Asset li.sbm-dropdown-item', $pnl.tmpls).clone();
}
ac.getFilterMenuOption = function (lbl, fn, cls)
{
return ac.getMenuOption(ac.getFilterDropdownTemplate(), lbl, fn, cls);
}
ac.getItemMenuOption = function (lbl, fn, cls)
{
return ac.getMenuOption(ac.getItemDropdownTemplate(), lbl, fn, cls);
}
ac.getMenuOption = function ($opt, lbl, fn, cls)
{
return $opt.find('div.sbm-label').text(lbl).end().on('click', fn).addClass(cls);
}
//--------------------------------------------------------------
ac.createItem = function ()
{
ac.clearItem();
sbm.populateSelectCodeDesc('#cbAssetStatus', sbm.data.statusesCreate);
$('#cbAssetStatus').val(sbm.data.statusCodeDefault).prop('readonly', false);
$dlg.item.dialog('option', 'title', sbm.dlg.title.createItem);
ac.openAssetDlg();
}
ac.openAssetDlg = function ()
{
sbm.openDialog($dlg.item, 600, 600);
}
ac.clearItem = function ()
{
let frm = $frm.item;
$('.AssetExists,.AssetId,input[type="text"],textarea,select', frm).val('');
sbm.populateSelectCodeDesc('#cbAssetStatus', sbm.data.statuses);
$('.AssetStatus', frm).prop('disabled', false);
sbm.clearControlErrors();
}
//--------------------------------------------------------------
ac.updateItem = function ($item, item)
{
ac.clearItem();
$('.AssetId', $frm.itemId).val(item.id);
sbm.ajax.updateItem = sbm.ajax.updateItem || { url: sbm.url.base + 'Assets/Asset/Ajax/GetItem', formId: $frm.itemId.prop('id'), callbackFn: ac.onUpdateItem };
sbm.doAjaxRequest(sbm.ajax.updateItem, 50);
}
ac.onUpdateItem = function (response)
{
sbm.debug('onUpdateItem', response);
let frm = $frm.item;
ac.clearItem();
if (!response.success) return;
let item = response.asset;
$('.AssetExists', frm).val(1);
$('.AssetId', frm).val(item.id);
$('.AssetCategoryId', frm).val(item.categoryId);
$('.AssetCategory', frm).val(item.categoryDesc);
$('.AssetSerialNumber', frm).val(item.serialNumber);
$('.AssetDescription', frm).val(item.descBrief);
$('.AssetNotes', frm).val(item.notes);
$('.AssetStatus', frm).val(item.statusCode).prop('disabled', true);
sbm.clearControlErrors();
$dlg.item.dialog('option', 'title', sbm.dlg.title.updateItem);
ac.openAssetDlg();
}
//--------------------------------------------------------------
ac.selectCategory = function ()
{
let fnHandleCategories = function (response)
{
//sbm.debug('fnHandleCategories', response)
if (!response.success) return;
let cfg = { caption: sbm.lbl.selectCategory, items: [], itemId: $('.AssetCategoryId', $frm.item).val(), fnCallback: fnItemSelected };
(response.assetCategories || []).forEach((ac, i) => { cfg.items.push({ id: ac.id, name: ac.descBrief }) });
sbm.fn.selectItem(cfg);
}
let fnItemSelected = function (item)
{
//sbm.debug('fnCallback', item);
$('.AssetCategoryId', $frm.item).val(item.id);
$('.AssetCategory', $frm.item).val(item.name);
}
ac.ajax.categories = ac.ajax.categories || { url: sbm.url.base + 'Assets/AssetCategory/Ajax/GetAssetCategories', formId: $frm.categories.prop('id'), callbackFn: fnHandleCategories };
sbm.doAjaxRequest(ac.ajax.categories, 50);
}
//--------------------------------------------------------------
ac.storeItem = function ()
{
sbm.ajax.storeItem = sbm.ajax.storeItem || { url: sbm.url.base + 'Assets/Asset/Ajax/StoreItem', formId: 'frmAsset', callbackFn: ac.onStoreItem };
sbm.doAjaxRequest(sbm.ajax.storeItem, 50);
}
ac.onStoreItem = function (response)
{
//sbm.debug('onStoreItem', response);
sbm.refreshControlValues(response.controlValues);
if (!response.success) return;
$dlg.item.dialog('close');
var item = response.asset;
var $item = $('#' + ac.getItemId(item));
if (!$item.length)
{
$item = ac.getItemRowTemplate();
$('table tbody', $pnl.table).prepend($item).show();
}
ac.populateItem($item, item);
// Force repopulation of sprts.
delete ac.data.assets;
}
//--------------------------------------------------------------
$(function ()
{
$pnl.items = $('.sbm-panel.Assets');
$pnl.header = $('.sbm-panel-header', $pnl.items);
$pnl.filter = $('.sbm-panel-filter', $pnl.items);
$pnl.msgs = $('.sbm-panel-messages', $pnl.items);
$pnl.table = $('.sbm-panel-item-table', $pnl.items);
$pnl.tmpls = $('.sbm-panel-item-template', $pnl.items);
$frm.filter = $('#frmAssetFilter');
$('.FilterSearch', $frm.filter).on('input', function () { ac.refreshResults(250); });
$frm.itemId = $('#frmAssetId');
$frm.categories = $('#frmCategories');
var opt = sbm.dlgOpts();
opt.buttons[sbm.lbl.save] = function () { ac.storeItem(); };
opt.buttons[sbm.lbl.cancel] = function () { $(this).dialog('close'); };
$dlg.item = $('#dlgAsset').dialog(opt);
$frm.item = $('#frmAsset');
$($frm.item).on('input', '.sbm-input, .sbm-textarea', function () { sbm.clearControlError(this); }).on('change', '.sbm-select', function () { sbm.clearControlError(this); });
$($frm.item).on('dblclick', '.sbm-input.AssetCategory', ac.selectCategory).on('click', '.sbm-action.AssetCategorySearch', ac.selectCategory);
setTimeout(ac.refreshResults, 250);
});
//--------------------------------------------------------------
}(window, window.document));
/* The End */
@@ -177,8 +177,8 @@
$frm.association = $('#frmAssociation');
var opt = sbm.dlgOpts();
opt.buttons[sbm.dlg.btn.ok] = function () { sbm.storeItem(); };
opt.buttons[sbm.dlg.btn.cancel] = function () { $(this).dialog('close'); };
opt.buttons[sbm.lbl.ok] = function () { sbm.storeItem(); };
opt.buttons[sbm.lbl.cancel] = function () { $(this).dialog('close'); };
sbm.$dlg.association = $('#dlgAssociation').dialog(opt);
setTimeout(sbm.refreshResults, 250);
@@ -0,0 +1,206 @@
/*
* SBM - Portal - Codes - Code Type.
*/
(function (win, doc, undef)
{
"use strict";
let ac = sbm.CodeCat = { data: {}, ajax: {} };
let $pnl = ac.$pnl = {}, $frm = ac.$frm = {}, $dlg = ac.$dlg = {};
//--------------------------------------------------------------
ac.refreshResults = function (delay)
{
if (!ac.awaitingRefreshResponse)
{
$('.sbm-burger', $pnl.header).invisible();
$pnl.table.find('tbody tr').remove();
$pnl.msgs.hide().empty().append($('<div class="sbm-message">' + sbm.msg.dataQuerying + '</div>')).fadeIn(200);
}
ac.ajax.codeTypes = ac.ajax.codeTypes || { url: sbm.url.base + 'Codes/CodeType/Ajax/GetCodeTypes', formId: $frm.filter.prop('id'), callbackFn: ac.handleCodeTypes };
sbm.doAjaxRequest(ac.ajax.codeTypes, delay || 50);
ac.awaitingRefreshResponse = true;
}
ac.handleCodeTypes = function (response)
{
//sbm.debug('handleCodeTypes', response);
delete ac.awaitingRefreshResponse;
let $table = $('table', $pnl.table).find('tbody tr').remove().end();
$pnl.msgs.hide().empty();
sbm.refreshPaging(response, '#frmCodeTypeFilter', ac.refreshResults);
if (!response.success) return;
// Reference data - Statuses.
if (response.statuses) sbm.data.statuses = response.statuses;
if (response.statusesCreate) sbm.data.statusesCreate = response.statusesCreate;
if (response.statusCodeDefault) sbm.data.statusCodeDefault = response.statusCodeDefault;
// Menu.
var $bgr = $('.sbm-burger', $pnl.header).invisible();
var $mnu = $('ul.sbm-dropdown-menu', $bgr).empty();
$mnu.append(ac.getFilterMenuOption(sbm.lbl.refresh, function () { ac.refreshResults(); }, 'sbm-refresh'));
$mnu.append(ac.getFilterMenuOption(sbm.lbl.create, function () { ac.createItem(); }, 'sbm-add'));
$bgr.toggleVisibility(!!$('.sbm-dropdown-item', $mnu).length);
// Items.
var items = response.codeTypes || [];
if (!items.length)
return $pnl.msgs.append($('<div class="sbm-message">' + sbm.msg.dataNoneFound + '</div>')).fadeIn(500);
for (let itemIdx = 0; itemIdx < items.length; itemIdx++)
$table.append(ac.populateItem(ac.getItemRowTemplate(), items[itemIdx]));
}
ac.populateItem = function ($item, item)
{
if (!item) return;
$pnl.msgs.hide().empty();
$item.prop('id', ac.getItemId(item));
$('.CodeTypeCode', $item).text(item.code);
$('.CodeTypeName', $item).text(item.name);
$('.CodeTypeCodes', $item).text(item.codeCount);
$('.CodeTypeStatusDesc', $item).text(item.statusDesc).removeClass(function (idx, name) { return (name.match(/(^|\s)sbm-status-\S+/g) || []).join(' '); }).addClass('sbm-status-' + item.statusCode.toLowerCase());
// Actions.
let fnUpd = function () { ac.updateItem($item, item) };
var $bgr = $('.sbm-burger', $item).invisible();
let $mnu = $('ul.sbm-dropdown-menu', $bgr).empty();
if (!item.deleted)
{
$mnu.append(ac.getItemMenuOption(sbm.lbl.edit, fnUpd, 'sbm-edit'));
$('.CodeTypeName', $item).on('click', fnUpd).addClass('sbm-action-text');
$('.CodeTypeCodes', $item).on('click', function () { sbm.navigateTo('Codes/Code?Filter.CodeTypeCode='+item.code);return false; }).on('dblclick', function () { return false; }).addClass('sbm-action-text');
$item.on('dblclick', fnUpd);
}
$bgr.toggleVisibility(!!$('.sbm-dropdown-item', $mnu).length);
return $item;
}
ac.getItemId = function (item)
{
return 'Itm_' + (item || {}).id;
}
//--------------------------------------------------------------
ac.getFilterDropdownTemplate = function ()
{
return $('.sbm-dropdown-menu.CodeTypeFilter li.sbm-dropdown-item', $pnl.tmpls).clone();
}
ac.getItemRowTemplate = function ()
{
return $('table tr.sbm-row-item.CodeType', $pnl.tmpls).clone();
}
ac.getItemDropdownTemplate = function ()
{
return $('.sbm-dropdown-menu.CodeType li.sbm-dropdown-item', $pnl.tmpls).clone();
}
ac.getFilterMenuOption = function (lbl, fn, cls)
{
return ac.getMenuOption(ac.getFilterDropdownTemplate(), lbl, fn, cls);
}
ac.getItemMenuOption = function (lbl, fn, cls)
{
return ac.getMenuOption(ac.getItemDropdownTemplate(), lbl, fn, cls);
}
ac.getMenuOption = function ($opt, lbl, fn, cls)
{
return $opt.find('div.sbm-label').text(lbl).end().on('click', fn).addClass(cls);
}
//--------------------------------------------------------------
ac.createItem = function ()
{
ac.clearItem();
sbm.populateSelectCodeDesc('#cbCodeTypeStatus', sbm.data.statusesCreate);
$('#cbCodeTypeStatus').val(sbm.data.statusCodeDefault).prop('readonly', false);
$dlg.item.dialog('option', 'title', sbm.dlg.title.createItem);
ac.openCodeTypeDlg();
}
ac.openCodeTypeDlg = function ()
{
sbm.openDialog($dlg.item, 600, 600);
}
ac.clearItem = function ()
{
let frm = $frm.item;
$('.CodeTypeExists,.CodeTypeId,input[type="text"],textarea,select', frm).val('');
sbm.populateSelectCodeDesc('#cbCodeTypeStatus', sbm.data.statuses);
$('.CodeTypeCode', frm).prop('readonly', false);
sbm.clearControlErrors();
}
//--------------------------------------------------------------
ac.updateItem = function ($item, item)
{
ac.clearItem();
$('.CodeTypeId', $frm.itemId).val(item.id);
sbm.ajax.updateItem = sbm.ajax.updateItem || { url: sbm.url.base + 'Codes/CodeType/Ajax/GetItem', formId: $frm.itemId.prop('id'), callbackFn: ac.onUpdateItem };
sbm.doAjaxRequest(sbm.ajax.updateItem, 50);
}
ac.onUpdateItem = function (response)
{
//sbm.debug('onUpdateItem', response);
let frm = $frm.item;
ac.clearItem();
if (!response.success) return;
let item = response.codeType;
$('.CodeTypeExists', frm).val(1);
$('.CodeTypeId', frm).val(item.id);
$('.CodeTypeCode', frm).val(item.code).prop('readonly', true);
$('.CodeTypeName', frm).val(item.name);
$('.CodeTypeNotes', frm).val(item.notes);
$('.CodeTypeStatus', frm).val(item.statusCode);
sbm.clearControlErrors();
$dlg.item.dialog('option', 'title', sbm.dlg.title.updateItem);
ac.openCodeTypeDlg();
}
//--------------------------------------------------------------
ac.storeItem = function ()
{
sbm.ajax.storeItem = sbm.ajax.storeItem || { url: sbm.url.base + 'Codes/CodeType/Ajax/StoreItem', formId: 'frmCodeType', callbackFn: ac.onStoreItem };
sbm.doAjaxRequest(sbm.ajax.storeItem, 50);
}
ac.onStoreItem = function (response)
{
//sbm.debug('onStoreItem', response);
sbm.refreshControlValues(response.controlValues);
if (!response.success) return;
$dlg.item.dialog('close');
var item = response.codeType;
var $item = $('#' + ac.getItemId(item));
if (!$item.length)
{
$item = ac.getItemRowTemplate();
$('table tbody', $pnl.table).prepend($item).show();
}
ac.populateItem($item, item);
// Force repopulation of sprts.
delete ac.data.codeTypes;
}
//--------------------------------------------------------------
$(function ()
{
$pnl.items = $('.sbm-panel.CodeTypes');
$pnl.header = $('.sbm-panel-header', $pnl.items);
$pnl.filter = $('.sbm-panel-filter', $pnl.items);
$pnl.msgs = $('.sbm-panel-messages', $pnl.items);
$pnl.table = $('.sbm-panel-item-table', $pnl.items);
$pnl.tmpls = $('.sbm-panel-item-template', $pnl.items);
$frm.filter = $('#frmCodeTypeFilter');
$('.FilterSearch', $frm.filter).on('input', function () { ac.refreshResults(250); });
$frm.itemId = $('#frmCodeTypeId');
var opt = sbm.dlgOpts();
opt.buttons[sbm.lbl.save] = function () { ac.storeItem(); };
opt.buttons[sbm.lbl.cancel] = function () { $(this).dialog('close'); };
$dlg.item = $('#dlgCodeType').dialog(opt);
$frm.item = $('#frmCodeType');
$($frm.item).on('input', '.sbm-input, .sbm-textarea', function () { sbm.clearControlError(this); }).on('change', '.sbm-select', function () { sbm.clearControlError(this); });
setTimeout(ac.refreshResults, 250);
});
//--------------------------------------------------------------
}(window, window.document));
/* The End */
@@ -0,0 +1,231 @@
/*
* SBM - Portal - Codes - Code.
*/
(function (win, doc, undef)
{
"use strict";
let ac = sbm.CodeCat = { data: {}, ajax: {} };
let $pnl = ac.$pnl = {}, $frm = ac.$frm = {}, $dlg = ac.$dlg = {};
//--------------------------------------------------------------
ac.refreshResults = function (delay)
{
if (!ac.awaitingRefreshResponse)
{
$('.sbm-burger', $pnl.header).invisible();
$pnl.table.find('tbody tr').remove();
$pnl.msgs.hide().empty().append($('<div class="sbm-message">' + sbm.msg.dataQuerying + '</div>')).fadeIn(200);
}
ac.ajax.codes = ac.ajax.codes || { url: sbm.url.base + 'Codes/Code/Ajax/GetCodes', formId: $frm.filter.prop('id'), callbackFn: ac.handleCodes };
sbm.doAjaxRequest(ac.ajax.codes, delay || 50);
ac.awaitingRefreshResponse = true;
}
ac.handleCodes = function (response)
{
sbm.debug('handleCodes', response);
delete ac.awaitingRefreshResponse;
let $table = $('table', $pnl.table).find('tbody tr').remove().end();
$pnl.msgs.hide().empty();
sbm.refreshPaging(response, '#frmCodeFilter', ac.refreshResults);
if (!response.success) return;
// Reference data - Statuses.
if (response.statuses) sbm.data.statuses = response.statuses;
if (response.statusesCreate) sbm.data.statusesCreate = response.statusesCreate;
if (response.statusCodeDefault) sbm.data.statusCodeDefault = response.statusCodeDefault;
// Menu.
var $bgr = $('.sbm-burger', $pnl.header).invisible();
var $mnu = $('ul.sbm-dropdown-menu', $bgr).empty();
$mnu.append(ac.getFilterMenuOption(sbm.lbl.refresh, function () { ac.refreshResults(); }, 'sbm-refresh'));
$mnu.append(ac.getFilterMenuOption(sbm.lbl.create, function () { ac.createItem(); }, 'sbm-add'));
$bgr.toggleVisibility(!!$('.sbm-dropdown-item', $mnu).length);
// Items.
var items = response.codes || [];
if (!items.length)
return $pnl.msgs.append($('<div class="sbm-message">' + sbm.msg.dataNoneFound + '</div>')).fadeIn(500);
for (let itemIdx = 0; itemIdx < items.length; itemIdx++)
$table.append(ac.populateItem(ac.getItemRowTemplate(), items[itemIdx]));
//for (let i = 1; i < 101; i++) $table.append($('<tr><td>'+i+'</td></tr>'));
}
ac.populateItem = function ($item, item)
{
if (!item) return;
$pnl.msgs.hide().empty();
$item.prop('id', ac.getItemId(item));
$('.CodeType', $item).text(item.typeName);
$('.CodeCode', $item).text(item.code);
$('.CodeDesc', $item).text(item.description);
$('.CodeStatusDesc', $item).text(item.statusDesc).removeClass(function (idx, name) { return (name.match(/(^|\s)sbm-status-\S+/g) || []).join(' '); }).addClass('sbm-status-' + item.statusCode.toLowerCase());
// Actions.
let fnUpd = function () { ac.updateItem($item, item) };
var $bgr = $('.sbm-burger', $item).invisible();
let $mnu = $('ul.sbm-dropdown-menu', $bgr).empty();
if (!item.deleted)
{
$mnu.append(ac.getItemMenuOption(sbm.lbl.edit, fnUpd, 'sbm-edit'));
$('.CodeDesc', $item).on('click', fnUpd).addClass('sbm-action-text');
$item.on('dblclick', fnUpd);
}
$bgr.toggleVisibility(!!$('.sbm-dropdown-item', $mnu).length);
return $item;
}
ac.getItemId = function (item)
{
return 'Itm_' + (item || {}).id;
}
//--------------------------------------------------------------
ac.getFilterDropdownTemplate = function ()
{
return $('.sbm-dropdown-menu.CodeFilter li.sbm-dropdown-item', $pnl.tmpls).clone();
}
ac.getItemRowTemplate = function ()
{
return $('table tr.sbm-row-item.Code', $pnl.tmpls).clone();
}
ac.getItemDropdownTemplate = function ()
{
return $('.sbm-dropdown-menu.Code li.sbm-dropdown-item', $pnl.tmpls).clone();
}
ac.getFilterMenuOption = function (lbl, fn, cls)
{
return ac.getMenuOption(ac.getFilterDropdownTemplate(), lbl, fn, cls);
}
ac.getItemMenuOption = function (lbl, fn, cls)
{
return ac.getMenuOption(ac.getItemDropdownTemplate(), lbl, fn, cls);
}
ac.getMenuOption = function ($opt, lbl, fn, cls)
{
return $opt.find('div.sbm-label').text(lbl).end().on('click', fn).addClass(cls);
}
//--------------------------------------------------------------
ac.createItem = function ()
{
ac.clearItem();
sbm.populateSelectCodeDesc('#cbCodeStatus', sbm.data.statusesCreate);
$('#cbCodeStatus').val(sbm.data.statusCodeDefault).prop('readonly', false);
$dlg.item.dialog('option', 'title', sbm.dlg.title.createItem);
ac.openCodeDlg();
}
ac.openCodeDlg = function ()
{
sbm.openDialog($dlg.item, 600, 600);
}
ac.clearItem = function ()
{
let frm = $frm.item;
$('.CodeExists,.CodeId,input[type="text"],textarea,select', frm).val('');
sbm.populateSelectCodeDesc('#cbCodeStatus', sbm.data.statuses);
$('.CodeStatus', frm).prop('disabled', false);
sbm.clearControlErrors();
}
//--------------------------------------------------------------
ac.updateItem = function ($item, item)
{
ac.clearItem();
$('.CodeId', $frm.itemId).val(item.id);
sbm.ajax.updateItem = sbm.ajax.updateItem || { url: sbm.url.base + 'Codes/Code/Ajax/GetItem', formId: $frm.itemId.prop('id'), callbackFn: ac.onUpdateItem };
sbm.doAjaxRequest(sbm.ajax.updateItem, 50);
}
ac.onUpdateItem = function (response)
{
sbm.debug('onUpdateItem', response);
let frm = $frm.item;
ac.clearItem();
if (!response.success) return;
let item = response.code;
$('.CodeExists', frm).val(1);
$('.CodeId', frm).val(item.id);
$('.CodeCategoryId', frm).val(item.categoryId);
$('.CodeCategory', frm).val(item.categoryDesc);
$('.CodeSerialNumber', frm).val(item.serialNumber);
$('.CodeDescription', frm).val(item.descBrief);
$('.CodeNotes', frm).val(item.notes);
$('.CodeStatus', frm).val(item.statusCode).prop('disabled', true);
sbm.clearControlErrors();
$dlg.item.dialog('option', 'title', sbm.dlg.title.updateItem);
ac.openCodeDlg();
}
//--------------------------------------------------------------
ac.selectCategory = function ()
{
let fnHandleCategories = function (response)
{
//sbm.debug('fnHandleCategories', response)
if (!response.success) return;
let cfg = { caption: sbm.lbl.selectCategory, items: [], itemId: $('.CodeCategoryId', $frm.item).val(), fnCallback: fnItemSelected };
(response.codeCategories || []).forEach((ac, i) => { cfg.items.push({ id: ac.id, name: ac.descBrief }) });
sbm.fn.selectItem(cfg);
}
let fnItemSelected = function (item)
{
//sbm.debug('fnCallback', item);
$('.CodeCategoryId', $frm.item).val(item.id);
$('.CodeCategory', $frm.item).val(item.name);
}
ac.ajax.categories = ac.ajax.categories || { url: sbm.url.base + 'Codes/CodeCategory/Ajax/GetCodeCategories', formId: $frm.categories.prop('id'), callbackFn: fnHandleCategories };
sbm.doAjaxRequest(ac.ajax.categories, 50);
}
//--------------------------------------------------------------
ac.storeItem = function ()
{
sbm.ajax.storeItem = sbm.ajax.storeItem || { url: sbm.url.base + 'Codes/Code/Ajax/StoreItem', formId: 'frmCode', callbackFn: ac.onStoreItem };
sbm.doAjaxRequest(sbm.ajax.storeItem, 50);
}
ac.onStoreItem = function (response)
{
//sbm.debug('onStoreItem', response);
sbm.refreshControlValues(response.controlValues);
if (!response.success) return;
$dlg.item.dialog('close');
var item = response.code;
var $item = $('#' + ac.getItemId(item));
if (!$item.length)
{
$item = ac.getItemRowTemplate();
$('table tbody', $pnl.table).prepend($item).show();
}
ac.populateItem($item, item);
// Force repopulation of sprts.
delete ac.data.codes;
}
//--------------------------------------------------------------
$(function ()
{
$pnl.items = $('.sbm-panel.Codes');
$pnl.header = $('.sbm-panel-header', $pnl.items);
$pnl.filter = $('.sbm-panel-filter', $pnl.items);
$pnl.msgs = $('.sbm-panel-messages', $pnl.items);
$pnl.table = $('.sbm-panel-item-table', $pnl.items);
$pnl.tmpls = $('.sbm-panel-item-template', $pnl.items);
$frm.filter = $('#frmCodeFilter');
$frm.filter.on('input', '.FilterSearch', function () { ac.refreshResults(250); }).on('change', '.FilterCodeTypeCode', function () { ac.refreshResults(250); });
$frm.itemId = $('#frmCodeId');
$frm.categories = $('#frmCategories');
var opt = sbm.dlgOpts();
opt.buttons[sbm.lbl.save] = function () { ac.storeItem(); };
opt.buttons[sbm.lbl.cancel] = function () { $(this).dialog('close'); };
$dlg.item = $('#dlgCode').dialog(opt);
$frm.item = $('#frmCode');
$($frm.item).on('input', '.sbm-input, .sbm-textarea', function () { sbm.clearControlError(this); }).on('change', '.sbm-select', function () { sbm.clearControlError(this); });
$($frm.item).on('dblclick', '.sbm-input.CodeCategory', ac.selectCategory).on('click', '.sbm-action.CodeCategorySearch', ac.selectCategory);
setTimeout(ac.refreshResults, 250);
});
//--------------------------------------------------------------
}(window, window.document));
/* The End */
@@ -177,8 +177,8 @@
$frm.commodity = $('#frmCommodity');
var opt = sbm.dlgOpts();
opt.buttons[sbm.dlg.btn.ok] = function () { sbm.storeItem(); };
opt.buttons[sbm.dlg.btn.cancel] = function () { $(this).dialog('close'); };
opt.buttons[sbm.lbl.ok] = function () { sbm.storeItem(); };
opt.buttons[sbm.lbl.cancel] = function () { $(this).dialog('close'); };
sbm.$dlg.commodity = $('#dlgCommodity').dialog(opt);
setTimeout(sbm.refreshResults, 250);
@@ -198,8 +198,8 @@
$frm.contact = $('#frmContact');
var opt = sbm.dlgOpts();
opt.buttons[sbm.dlg.btn.ok] = function () { sbm.storeItem(); };
opt.buttons[sbm.dlg.btn.cancel] = function () { $(this).dialog('close'); };
opt.buttons[sbm.lbl.ok] = function () { sbm.storeItem(); };
opt.buttons[sbm.lbl.cancel] = function () { $(this).dialog('close'); };
$dlg.contact = $('#dlgContact').dialog(opt);
setTimeout(sbm.refreshResults, 250);
@@ -21,6 +21,10 @@
{
return !isNaN(parseFloat(n)) && isFinite(n);
}
sbm.getTime = function ()
{
return (new Date()).getTime();
}
sbm.zeroPad = function (num, count)
{
var val = num + '';
@@ -52,12 +56,12 @@
if (file.filename) return filename.split('.').pop();
return "";
}
sbm.formatFileSize = function (size)
sbm.formatFileSize = function (size, decimals = 0)
{
if (size / 10 ** 9 > 1) return Math.floor(size / 10 ** 9) + 'Gb';
if (size / 10 ** 6 > 1) return Math.floor(size / 10 ** 6) + 'Mb';
if (size / 10 ** 3 > 1) return Math.floor(size / 10 ** 3) + 'Kb';
return size + 'bytes';
if (size / 10 ** 9 > 1) return (size / 10.0 ** 9).toFixed(decimals) + ' Gb';
if (size / 10 ** 6 > 1) return (size / 10.0 ** 6).toFixed(decimals) + ' Mb';
if (size / 10 ** 3 > 1) return (size / 10.0 ** 3).toFixed(decimals) + ' Kb';
return size + ' bytes';
}
sbm.escapeRegExp = function (val)
{
@@ -70,6 +74,21 @@
}
sbm.debug = sbm.debugMode ? win.console.log.bind(win.console) : function () { };
//--------------------------------------------------------------
sbm.compareCI = function (a, b)
{
var comparison = a.toLowerCase().localeCompare(b.toLowerCase());
if (comparison === 0)
return a.localeCompare(b);
return comparison;
}
sbm.arraySortCI = function ()
{
if (!this) return this;
this.sort(function (a, b) { return sbm.compareCI(a, b); });
return this;
};
//--------------------------------------------------------------
sbm.setOddDataRowClass = function (rowSel, cName)
{
@@ -78,35 +97,49 @@
return index % 2 == 1 ? (cName || "other") : "";
});
}
sbm.populateSelect = function ($sel, values, code, desc)
{
code = code || 'code';
desc = desc || 'desc';
$sel = $($sel).empty();
for (const value of values || [])
$sel.append($('<option>').prop('value', value[code]).text(value[desc]));
}
sbm.populateSelectCodeDesc = function ($sel, values)
{
$sel = $($sel).empty();
for (const value of values)
$sel.append($('<option>').prop('value', value.code).text(value.desc));
sbm.populateSelect($sel, values);
}
sbm.checkTextAreaMaxLength = function (ctrl, len)
{
if (ctrl == null || ctrl.value == null) return;
if (ctrl.value.length > len) ctrl.value = ctrl.value.substring(0, len);
}
sbm.setAvailableCharsLabel = function ($if, max, $lbl)
sbm.setAvailableCharsInfo = function ($if, max, $info)
{
var avail = Math.max(0, max - $if.val().length);
$lbl.text(avail == 1 ? sbm.lbl.availableCharacter : sbm.lbl.availableCharacters.replace(/\{0\}/g, avail));
let info = '';
if (max > 0)
{
let avail = Math.max(0, max - $if.val().length);
info = (avail == 1 ? sbm.lbl.charAvailable : sbm.lbl.charsAvailable.replace(/\{0\}/g, avail));
}
($info || $('.sbm-info', $if.closest('.sbm-form-control'))).text(info);
}
sbm.setCharsAvailableLabel = function ($if, max)
{
var avail = Math.max(0, max - $if.val().length);
$('.sbm-select-chars-available', $if.closest('.sbm-form-control')).text(avail == 1 ? sbm.lbl.availableCharacter : sbm.lbl.availableCharacters.replace(/\{0\}/g, avail));
}
sbm.setItemsSelectedLabel = function ($sel)
sbm.setItemsSelectedInfo = function ($sel, $info)
{
var sel = $(':selected', $sel).length;
$('.sbm-select-items-selected', $sel.closest('.sbm-form-control')).text(sel == 0 ? sbm.lbl.noneSelected : sbm.lbl.numberSelected.replace(/\{0\}/g, sel));
($info || $('.sbm-info', $sel.closest('.sbm-form-control'))).text(sel == 0 ? sbm.lbl.noneSelected : sbm.lbl.numberSelected.replace(/\{0\}/g, sel));
}
sbm.initCalendars = function (sel)
sbm.initCalendars = function (sel, opt)
{
return $(sel).datepicker();
return $(sel).datepicker(opt || {});
}
sbm.allowClearCalendar = function (sel)
{
$(sel).datepicker().on('keyup', function (e)
{
if (e.key == 'Backspace' || e.key == 'Delete')
$(this).datepicker('setDate', null).datepicker('hide').trigger('change');
});
}
sbm.refreshPaging = function (response, context, refreshFn)
{
@@ -119,7 +152,7 @@
sbm.navigateTo = function (action)
{
sbm.doPleaseWait();
$('#frmNavigate').prop('action', sbm.url.base + action).submit();
$('#frmNavigate').prop('action', sbm.url.base + action).trigger('submit');
}
sbm.goToPage = function (action)
{
@@ -127,17 +160,6 @@
location = sbm.url.base + action;
}
// Account action.
sbm.onAccountAction = function (select)
{
var action = $(select).val();
$(select).val("");
if (action == "Profile")
sbm.navigateTo("Profile");
else if (action == "Organisations")
sbm.navigateTo("Organisations");
}
// Security.
sbm.redirectHome = function ()
{
@@ -171,12 +193,12 @@
}
sbm.dlgOpts = function ()
{
return { autoOpen: false, modal: true, show: 'clip', hide: 'clip', minWidth: 320, maxWidth: 480, width: 'auto', resizable: false, draggable: false, closeOnEscape: false, dialogClass: "dialog-no-close", buttons: {} };
return { autoOpen: false, modal: true, show: 'clip', hide: 'clip', minWidth: 320, maxWidth: 480, width: 'auto', resizable: false, draggable: false, closeOnEscape: false, dialogClass: "no-close", buttons: {} };
}
sbm.dlgOptsClose = function ()
{
var opts = sbm.dlgOpts();
opts.buttons[sbm.dlg.btn.close] = function () { $(this).dialog('close'); };
opts.buttons[sbm.lbl.close] = function () { $(this).dialog('close'); };
return opts;
}
sbm.doAlert = function (title, msg, onOk)
@@ -189,11 +211,12 @@
if (!sbm.isDef(sbm.$dlg.alert))
{
var opt = sbm.dlgOpts();
opt.buttons[sbm.dlg.btn.ok] = function () { $(this).dialog('close'); if (onOk) onOk(); };
sbm.$dlg.alert = $('<div id="dlgAlert"><div id="dlgAlertMsg"></div></div>').dialog(opt);
}
$('#dlgAlertMsg', sbm.$dlg.alert).html(msg);
sbm.$dlg.alert.dialog('option', { title: title || sbm.dlg.title.alert });
$('#dlgAlertMsg', sbm.$dlg.alert).html(msg.replace(/\\n/g, '<br>'));
let btns = [];
btns[sbm.lbl.ok] = function () { $(this).dialog('close'); if (onOk) onOk(); };
sbm.$dlg.alert.dialog('option', { title: title || sbm.dlg.title.alert, buttons: btns });
sbm.openDialog(sbm.$dlg.alert);
}
sbm.doConfirm = function (title, msg, onOk, onCancel)
@@ -208,11 +231,11 @@
var opt = sbm.dlgOpts();
sbm.$dlg.confirm = $('<div id="dlgConfirm"><div class="spacer2"></div><div id="dlgConfirmMsg"></div></div>').dialog(opt);
}
var buttons = sbm.$dlg.confirm.dialog('option', 'buttons');
buttons[sbm.dlg.btn.yes] = function () { $(this).dialog('close'); if (onOk) onOk(); };
buttons[sbm.dlg.btn.no] = function () { $(this).dialog('close'); if (onCancel) onCancel(); };
sbm.$dlg.confirm.dialog('option', { title: title || sbm.dlg.title.confirm, 'buttons': buttons });
$('#dlgConfirmMsg').html(msg);
var btns = [];
btns[sbm.lbl.yes] = function () { $(this).dialog('close'); if (onOk) onOk(); };
btns[sbm.lbl.no] = function () { $(this).dialog('close'); if (onCancel) onCancel(); };
sbm.$dlg.confirm.dialog('option', { title: title || sbm.dlg.title.confirm, buttons: btns });
$('#dlgConfirmMsg').html(msg.replace(/\\n/g, '<br>'));
sbm.openDialog(sbm.$dlg.confirm);
}
sbm.confirmItemRemove = function (fn)
@@ -234,7 +257,7 @@
opt.hide = { effect: 'fade', duration: 200 };
sbm.$dlg.progress = $('<div id="dlgProgress"><div id="dlgProgressMsg"></div></div>').dialog(opt);
}
$('#dlgProgressMsg').html(msg);
$('#dlgProgressMsg').html(msg.replace(/\\n/g, '<br>'));
sbm.$dlg.progress.dialog('option', { title: title || sbm.dlg.title.progress });
if (delay > 0) sbm.toShowProgress = setTimeout(function () { sbm.$dlg.progress.dialog('open'); }, delay);
else sbm.$dlg.progress.dialog('open');
@@ -479,22 +502,25 @@
sbm.onWinResizePage = function () { };
sbm.onWinResize = function ()
{
var winH = $(win).height();
var $pageBody = $('#sbm-page-body');
var $hdr = $('#sbm-page-header');
var $ftr = $('#sbm-page-footer');
if (sbm.isAutoHeight())
$pageBody.height('auto');
else
$pageBody.innerHeight(Math.min(1200, winH - $hdr.outerHeight() - $ftr.outerHeight(true)));
$ftr.css('visibility', 'visible');
$('body').toggleClass('sbm-layout-flex', !sbm.isAutoHeight()).toggleClass('sbm-layout-flow', sbm.isAutoHeight());
sbm.onWinResizePage();
}
setTimeout(sbm.onWinResize, 50);
//--------------------------------------------------------------
$(function ()
{
$(win).on('resize', function () { sbm.onWinResize(); });
$('.sbm-button-cta,.sbm-button-action,.sbm-action-link,.sbm-social-icon.sbm-action').prop('tabindex', 0);
$.fn.visible = function () { return this.css('visibility', 'visible'); };
$.fn.invisible = function () { return this.css('visibility', 'hidden'); };
$.fn.toggleVisibility = function ()
{
return this.css('visibility', function (i, visibility) { return (visibility == 'visible') ? 'hidden' : 'visible'; });
};
if (sbm.keepLoggedIn)
sbm.configureKeepLoggedIn();
else if (!sbm.awaitingLogin)
@@ -71,12 +71,13 @@
//--------------------------------------------------------------
$(function ()
{
$(':input:enabled:visible:first').focus();
$(':input:enabled:visible:first').trigger('focus');
$('#ifEmailAddress,#ifPasswordCurrent,#ifPassword,#ifPasswordConfirm').on('input', function () { sbm.clearControlError(this); });
$('#frmChangePassword #ifPassword').on('keyup', sbm.doScorePassword);
sbm.$prgPasswordStrength = $('#frmChangePassword #prgPasswordStrength').progressbar({ 'max': 5 });
$('#ifEmailAddress,#ifPassword', $('#frmSignIn')).on('keypress', function (e) { if (e.which == 13) sbm.doSignin(); });
$('#frmSignIn #pbSignIn').on('click', function () { sbm.doSignin(); });
sbm.$frm.signIn = $('#frmSignIn');
sbm.$frm.signIn.on('keypress', '#ifEmailAddress,#ifPassword,#chkKeepMeSignedIn', function (e) { if (e.which == 13 && $('#ifEmailAddress', sbm.$frm.signIn).val() && $('#ifPassword', sbm.$frm.signIn).val()) sbm.doSignin(); });
sbm.$frm.signIn.on('click', '#pbSignIn', function () { sbm.doSignin(); }).on('keypress', '#pbSignIn', function (e) { if (e.which == 13) sbm.doSignin(); });;
$('#ifPasswordCurrent,#ifPassword,#ifPasswordConfirm', $('#frmChangePassword')).on('keypress', function (e) { if (e.which == 13) sbm.doChangePassword(); });
$('#frmChangePassword #pbChangePassword').on('click', function () { sbm.doChangePassword(); });
$('#ifEmailAddress', $('#frmRequestPassword')).on('keypress', function (e) { if (e.which == 13) sbm.doRequestPassword(); });
@@ -0,0 +1,123 @@
/*
* Portal - Item selection.
*/
(function (win, doc, undef)
{
"use strict";
let si = sbm.data.selectItem = sbm.data.selectItem || { items: {}, fn: {}, $dlg: {}, itemIdPrefix: 'Itm_' };
//--------------------------------------------------------------
/*
* Item selection config:
* - caption - dialog title.
* - items - list of objects: { id, name }
* - itemId - currently selected item.
* - fnCallback - method to call on completion to store selected item.
*/
sbm.fn.selectItem = function (cfg)
{
si.cfg = cfg;
si.itemId = cfg.itemId;
si.fn.showSelectItem();
}
si.fn.showSelectItem = function ()
{
$('.SelectedItemName', si.$dlg).text(si.fn.getItemName(si.itemId));
$('#ifItemFilter', si.$dlg).val('');
si.fn.displayItems();
sbm.openDialog(si.$dlg.dialog('option', { title: si.cfg.caption || 'Select Item' }), 480, 640);
}
//--------------------------------------------------------------
si.fn.compareItems = function (a, b)
{
var comparison = a.name.toLowerCase().localeCompare(b.name.toLowerCase());
if (comparison === 0)
return a.name.localeCompare(b.name);
return comparison;
}
//--------------------------------------------------------------
si.fn.displayItems = function ()
{
let filter = $('#ifItemFilter', si.$dlg).val();
let reFilter = new RegExp(sbm.escapeRegExp((filter || '').trim()), 'i');
let $itemsAvail = $('.sbm-selectItem-items.AvailableItems').empty();
let $itemTmpl = $('.sbm-panel-item-template .sbm-panel-item.Item');
let items = [...si.cfg.items];
items.sort(si.fn.compareItems);
for (let rgnIdx = 0; rgnIdx < items.length; rgnIdx++)
{
let item = items[rgnIdx];
let id = item.id;
if (filter && !item.name.match(reFilter)) continue;
let $item = $itemTmpl.clone();
$item.prop('id', si.itemIdPrefix + id).on('click', function () { si.fn.onClickItem(id); }).on('dblclick', function () { si.fn.onDblClickItem(id); }).find('.ItemName').text(item.name).end();
if (id == si.itemId) $item.addClass('sel');
$itemsAvail.append($item);
}
}
si.fn.getItem = function (id)
{
for (let rgnIdx = 0; rgnIdx < si.cfg.items.length; rgnIdx++)
{
let item = si.cfg.items[rgnIdx];
if (item.id == id) return item;
}
}
si.fn.getItemName = function (id)
{
let item = si.fn.getItem(id);
return item ? item.name : '';
}
//--------------------------------------------------------------
si.fn.onClickItem = function (id)
{
$('.SelectedItemName', si.$dlg).text(si.fn.getItemName(si.itemId = id));
$('.sbm-panel-item.Item').removeClass('sel');
$('.sbm-panel-item.Item#' + si.itemIdPrefix + id).addClass('sel');
}
si.fn.onDblClickItem = function (id)
{
si.itemId = id;
si.fn.storeSelectItem();
}
si.fn.storeSelectItem = function ()
{
if (!si.itemId) return;
let item = si.fn.getItem(si.itemId);
if (!item) return;
if (si.cfg.fnCallback) si.cfg.fnCallback(item);
si.$dlg.dialog('close');
si.fn.cleanUp();
}
si.fn.cleanUp = function ()
{
delete si.cfg;
delete si.itemId;
}
//--------------------------------------------------------------
si.fn.onDlgSize = function ()
{
let $pnl = $('.sbm-selectItem-items.AvailableItems');
$pnl.outerHeight(Math.floor(si.$dlg.innerHeight() - $pnl.position().top - ($pnl.outerHeight(true) - $pnl.innerHeight())));
}
//--------------------------------------------------------------
$(function ()
{
var opt = sbm.dlgOpts();
opt.open = function (evt, ui) { si.fn.onDlgSize(); };
opt.resize = function (evt, ui) { si.fn.onDlgSize(); };
opt.buttons[sbm.lbl.ok] = function () { si.fn.storeSelectItem(); };
opt.buttons[sbm.lbl.cancel] = function () { si.fn.cleanUp(); $(this).dialog('close'); };
si.$dlg = $('#dlgSelectItem').dialog(opt);
});
//--------------------------------------------------------------
}(window, window.document));
/* The End */
@@ -0,0 +1,207 @@
/*
* SBM - Portal - Assets - Vehicle Class.
*/
(function (win, doc, undef)
{
"use strict";
let vc = sbm.VehicleClass = { data: {}, ajax: {} };
let $pnl = vc.$pnl = {}, $frm = vc.$frm = {}, $dlg = vc.$dlg = {};
//--------------------------------------------------------------
vc.refreshResults = function (delay)
{
if (!vc.awaitingRefreshResponse)
{
$('.sbm-burger', $pnl.header).invisible();
$pnl.table.find('tbody tr').remove();
$pnl.msgs.hide().empty().append($('<div class="sbm-message">' + sbm.msg.dataQuerying + '</div>')).fadeIn(200);
}
vc.ajax.vehicleClasses = vc.ajax.vehicleClasses || { url: sbm.url.base + 'Assets/VehicleClass/Ajax/GetVehicleClasses', formId: $frm.filter.prop('id'), callbackFn: vc.handleVehicleClasses };
sbm.doAjaxRequest(vc.ajax.vehicleClasses, delay || 50);
vc.awaitingRefreshResponse = true;
}
vc.handleVehicleClasses = function (response)
{
sbm.debug('handleVehicleClasses', response);
delete vc.awaitingRefreshResponse;
let $table = $('table', $pnl.table).find('tbody tr').remove().end();
$pnl.msgs.hide().empty();
sbm.refreshPaging(response, '#frmVehicleClassFilter', vc.refreshResults);
if (!response.success) return;
// Reference data - Statuses.
if (response.statuses) sbm.data.statuses = response.statuses;
if (response.statusesCreate) sbm.data.statusesCreate = response.statusesCreate;
if (response.statusCodeDefault) sbm.data.statusCodeDefault = response.statusCodeDefault;
// Menu.
var $bgr = $('.sbm-burger', $pnl.header).invisible();
var $mnu = $('ul.sbm-dropdown-menu', $bgr).empty();
$mnu.append(vc.getFilterMenuOption(sbm.lbl.refresh, function () { vc.refreshResults(); }, 'sbm-refresh'));
$mnu.append(vc.getFilterMenuOption(sbm.lbl.create, function () { vc.createItem(); }, 'sbm-add'));
$bgr.toggleVisibility(!!$('.sbm-dropdown-item', $mnu).length);
// Items.
var items = response.vehicleClasses || [];
if (!items.length)
return $pnl.msgs.append($('<div class="sbm-message">' + sbm.msg.dataNoneFound + '</div>')).fadeIn(500);
for (let itemIdx = 0; itemIdx < items.length; itemIdx++)
$table.append(vc.populateItem(vc.getItemRowTemplate(), items[itemIdx]));
}
vc.populateItem = function ($item, item)
{
if (!item) return;
$pnl.msgs.hide().empty();
$item.prop('id', vc.getItemId(item));
$('.VehicleClassMake', $item).text(item.makeDesc);
$('.VehicleClassModel', $item).text(item.model);
$('.VehicleClassDerivative', $item).text(item.derivative);
$('.VehicleClassDescription', $item).text(item.descBrief);
$('.VehicleClassStatusDesc', $item).text(item.statusDesc).removeClass(function (idx, name) { return (name.match(/(^|\s)sbm-status-\S+/g) || []).join(' '); }).addClass('sbm-status-' + item.statusCode.toLowerCase());
// Actions.
let fnUpd = function () { vc.updateItem($item, item) };
var $bgr = $('.sbm-burger', $item).invisible();
let $mnu = $('ul.sbm-dropdown-menu', $bgr).empty();
if (!item.deleted)
{
$mnu.append(vc.getItemMenuOption(sbm.lbl.edit, fnUpd, 'sbm-edit'));
$('.VehicleClassMake,.VehicleClassModel', $item).off('click').on('click', fnUpd).addClass('sbm-action-text');
$item.off('dblclick').on('dblclick', fnUpd);
}
$bgr.toggleVisibility(!!$('.sbm-dropdown-item', $mnu).length);
return $item;
}
vc.getItemId = function (item)
{
return 'Itm_' + (item || {}).id;
}
//--------------------------------------------------------------
vc.getFilterDropdownTemplate = function ()
{
return $('.sbm-dropdown-menu.VehicleClassFilter li.sbm-dropdown-item', $pnl.tmpls).clone();
}
vc.getItemRowTemplate = function ()
{
return $('table tr.sbm-row-item.VehicleClass', $pnl.tmpls).clone();
}
vc.getItemDropdownTemplate = function ()
{
return $('.sbm-dropdown-menu.VehicleClass li.sbm-dropdown-item', $pnl.tmpls).clone();
}
vc.getFilterMenuOption = function (lbl, fn, cls)
{
return vc.getMenuOption(vc.getFilterDropdownTemplate(), lbl, fn, cls);
}
vc.getItemMenuOption = function (lbl, fn, cls)
{
return vc.getMenuOption(vc.getItemDropdownTemplate(), lbl, fn, cls);
}
vc.getMenuOption = function ($opt, lbl, fn, cls)
{
return $opt.find('div.sbm-label').text(lbl).end().on('click', fn).addClass(cls);
}
//--------------------------------------------------------------
vc.createItem = function ()
{
vc.clearItem();
sbm.populateSelectCodeDesc('#cbVehicleClassStatus', sbm.data.statusesCreate);
$('#cbVehicleClassStatus').val(sbm.data.statusCodeDefault).prop('readonly', false);
$dlg.item.dialog('option', 'title', sbm.dlg.title.createItem);
vc.openVehicleClassDlg();
}
vc.openVehicleClassDlg = function ()
{
sbm.openDialog($dlg.item, 600, 600);
}
vc.clearItem = function ()
{
let frm = $frm.item;
$('.VehicleClassExists,.VehicleClassId,input[type="text"],textarea,select', frm).val('');
sbm.populateSelectCodeDesc('#cbVehicleClassStatus', sbm.data.statuses);
$('.VehicleClassType,.VehicleClassStatus', frm).prop('disabled', false);
sbm.clearControlErrors();
}
//--------------------------------------------------------------
vc.updateItem = function ($item, item)
{
vc.clearItem();
$('.VehicleClassId', $frm.itemId).val(item.id);
sbm.ajax.updateItem = sbm.ajax.updateItem || { url: sbm.url.base + 'Assets/VehicleClass/Ajax/GetItem', formId: $frm.itemId.prop('id'), callbackFn: vc.onUpdateItem };
sbm.doAjaxRequest(sbm.ajax.updateItem, 50);
}
vc.onUpdateItem = function (response)
{
//sbm.debug('onUpdateItem', response);
let frm = $frm.item;
vc.clearItem();
if (!response.success) return;
let item = response.vehicleClass;
$('.VehicleClassExists', frm).val(1);
$('.VehicleClassId', frm).val(item.id);
$('.VehicleClassType', frm).val(item.typeCode).prop('disabled', true);
$('.VehicleClassCode', frm).val(item.code);
$('.VehicleClassDescription', frm).val(item.descBrief);
$('.VehicleClassNotes', frm).val(item.notes);
$('.VehicleClassStatus', frm).val(item.statusCode).prop('disabled', true);
sbm.clearControlErrors();
$dlg.item.dialog('option', 'title', sbm.dlg.title.updateItem);
vc.openVehicleClassDlg();
}
//--------------------------------------------------------------
vc.storeItem = function ()
{
sbm.ajax.storeItem = sbm.ajax.storeItem || { url: sbm.url.base + 'Assets/VehicleClass/Ajax/StoreItem', formId: 'frmVehicleClass', callbackFn: vc.onStoreItem };
sbm.doAjaxRequest(sbm.ajax.storeItem, 50);
}
vc.onStoreItem = function (response)
{
//sbm.debug('onStoreItem', response);
sbm.refreshControlValues(response.controlValues);
if (!response.success) return;
$dlg.item.dialog('close');
var item = response.vehicleClass;
var $item = $('#' + vc.getItemId(item));
if (!$item.length)
{
$item = vc.getItemRowTemplate();
$('table tbody', $pnl.table).prepend($item).show();
}
vc.populateItem($item, item);
// Force repopulation of sprts.
delete vc.data.vehicleClasses;
}
//--------------------------------------------------------------
$(function ()
{
$pnl.items = $('.sbm-panel.VehicleClasses');
$pnl.header = $('.sbm-panel-header', $pnl.items);
$pnl.filter = $('.sbm-panel-filter', $pnl.items);
$pnl.msgs = $('.sbm-panel-messages', $pnl.items);
$pnl.table = $('.sbm-panel-item-table', $pnl.items);
$pnl.tmpls = $('.sbm-panel-item-template', $pnl.items);
$frm.filter = $('#frmVehicleClassFilter');
$('.FilterSearch', $frm.filter).on('input', function () { vc.refreshResults(250); });
$frm.itemId = $('#frmVehicleClassId');
var opt = sbm.dlgOpts();
opt.buttons[sbm.lbl.save] = function () { vc.storeItem(); };
opt.buttons[sbm.lbl.cancel] = function () { $(this).dialog('close'); };
$dlg.item = $('#dlgVehicleClass').dialog(opt);
$frm.item = $('#frmVehicleClass');
$($frm.item).on('input', '.sbm-input, .sbm-textarea', function () { sbm.clearControlError(this); }).on('change', '.sbm-select', function () { sbm.clearControlError(this); });
setTimeout(vc.refreshResults, 250);
});
//--------------------------------------------------------------
}(window, window.document));
/* The End */