Web app logon.

This commit is contained in:
Andy Howard
2024-06-04 16:16:05 +01:00
parent f5e0b925fb
commit 0c44e9ddd3
70 changed files with 2624 additions and 1331 deletions
@@ -117,6 +117,18 @@
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<data name="association.caption.association" xml:space="preserve">
<value>Association</value>
</data>
<data name="association.caption.association.create" xml:space="preserve">
<value>Create Association</value>
</data>
<data name="association.caption.association.update" xml:space="preserve">
<value>Update Association</value>
</data>
<data name="association.caption.associations" xml:space="preserve">
<value>Associations</value>
</data>
<data name="commodity.caption.commodities" xml:space="preserve">
<value>Commodities</value>
</data>
@@ -156,6 +168,9 @@
<data name="label.any" xml:space="preserve">
<value>Any</value>
</data>
<data name="label.association" xml:space="preserve">
<value>Association</value>
</data>
<data name="label.associations" xml:space="preserve">
<value>Associations</value>
</data>
@@ -189,6 +204,9 @@
<data name="label.contacts" xml:space="preserve">
<value>Contacts</value>
</data>
<data name="label.context" xml:space="preserve">
<value>Context</value>
</data>
<data name="label.create" xml:space="preserve">
<value>Create</value>
</data>
@@ -198,6 +216,9 @@
<data name="label.create.organisation" xml:space="preserve">
<value>Create Organisation</value>
</data>
<data name="label.customers" xml:space="preserve">
<value>Customers</value>
</data>
<data name="label.description" xml:space="preserve">
<value>Description</value>
</data>
@@ -279,6 +300,9 @@
<data name="label.signout" xml:space="preserve">
<value>Sign-out</value>
</data>
<data name="label.Suppliers" xml:space="preserve">
<value>Suppliers</value>
</data>
<data name="label.type" xml:space="preserve">
<value>Type</value>
</data>
@@ -18,142 +18,164 @@ using System.Threading.Tasks;
namespace Sarand.BusinessMgr.Net.Core.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>();
// Session contexts.
private static ushort sessionContextIdx_Next = 100;
private ushort sessionContextIdx_Control = 0;
private class SessionContextData
{
public SessionContextData(SbmSessionContext_Web sessionContext)
{
SessionContext = sessionContext;
}
public SbmSessionContext_Web SessionContext { get; }
public ISacsDatabase ControlDatabase { get; set; }
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;
// 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>();
//-------------------------------------------------------------------
// 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 exc;
}
}
//-------------------------------------------------------------------
// 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;
}
}
//-------------------------------------------------------------------
// 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(SbmSessionContext sessionContext)
{
return sessionContexts.GetValue(sessionContext.Index, null)?.ControlDatabase;
}
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.
// 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); } }
//-------------------------------------------------------------------
// 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)
{
String fn = GetType().Name + ".InitEnvironment";
Development = env.IsDevelopment();
environmentName = env.EnvironmentName;
SaLogger.LogFilenamePrefix = "Sbm-Web-Onl";
SaLogger.IncludeHostInFileMessage = false;
SaLogger.IncludeUserInFileMessage = false;
//-------------------------------------------------------------------
// 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)
{
String fn = GetType().Name + ".InitEnvironment";
Development = env.IsDevelopment();
environmentName = env.EnvironmentName;
SaLogger.LogFilenamePrefix = "Sbm-Web-Onl";
SaLogger.IncludeHostInFileMessage = false;
SaLogger.IncludeUserInFileMessage = false;
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();
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 configuration.
appConfigs = SbmAppConfigHelper.ExtractAppConfigs(SbmConfiguration.GetSection("AppConfigs"));
emailConfigs = SbmEmailConfigHelper.ExtractEmailConfigs(SbmConfiguration.GetSection("EmailConfigs"));
LoggingRootFolder = AppConfig.Logging.RootFolder;
// Application configuration.
appConfigs = SbmAppConfigHelper.ExtractAppConfigs(SbmConfiguration.GetSection("AppConfigs"));
emailConfigs = SbmEmailConfigHelper.ExtractEmailConfigs(SbmConfiguration.GetSection("EmailConfigs"));
LoggingRootFolder = AppConfig.Logging.RootFolder;
// 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 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(fn);
// 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(fn);
// Configure database connection.
SacsDatabaseConsole.OdbcConfig config = new SacsDatabaseConsole.OdbcConfig();
config.location = AppConfig.BusMgrDatabase.Location;
config.user = AppConfig.BusMgrDatabase.User;
config.pword = AppConfig.BusMgrDatabase.Password;
SacsDatabase coreDatabase = SacsDatabaseConsole.OpenOdbcDatabase(config);
if (coreDatabase.HasErrorState())
SaLogger.Warn(fn + " Database initialisation error: " + coreDatabase.ErrorStateMessage);
// 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(fn + " Control Database initialisation error: " + controlDatabase.ErrorStateMessage);
// Session context - only one context available/necessary (for now).
sessionContextIdx_Control = sessionContextIdx_Next++;
SessionContextData sessionContextData = new SessionContextData(new SbmSessionContext_Web(sessionContextIdx_Control, Sbm.Application.WebPortal));
sessionContextData.BusMgrDatabase = coreDatabase;
sessionContexts.Add(sessionContextData.SessionContext.Index, sessionContextData);
}
catch (Exception exc)
{
SaLogger.Error(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.AddRange(dbConfig.Identifiers);
sessionContexts.Add(sessionContextData.SessionContext.Index, sessionContextData);
}
else SaLogger.Warn(fn + " Database: " + coreDatabase.DatabaseName + ". Initialisation error: " + coreDatabase.ErrorStateMessage);
}
}
catch (Exception exc)
{
SaLogger.Error(fn + " " + ExceptionUtil.GetLogMessage(exc));
}
}
//-------------------------------------------------------------------
}
//=======================================================================
//-------------------------------------------------------------------
}
//=======================================================================
}
@@ -11,6 +11,7 @@ using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using static Sarand.BusinessMgr.Net.Standard.Base.App.Sbm.Config.Database;
namespace Sarand.BusinessMgr.Net.Core.Web.Portal.App
{
@@ -22,15 +23,16 @@ namespace Sarand.BusinessMgr.Net.Core.Web.Portal.App
[JsonConstructor]
private SbmSessionData_Portal(long sessionIdx, int itemIdx, SbmSessionContext sessionContext) :
base(sessionIdx, itemIdx, sessionContext)
{ }
{}
/// <summary>
/// SessionData construction.
/// <para>SessionContext and UserAccount must be supplied.</para>
/// <para>Organisation is optional.</para>
/// </summary>
public SbmSessionData_Portal(SbmSessionContext sessionContext, SaSecurityItem securityItemUser, SbmAffiliate affiliateUser, List<SysIdString> supervisedOrganisations, SbmAssociation associationOrganisation, SbmAffiliate affiliateOrganisation) :
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)
{
DbIdentifier = dbIdentifier;
}
//-------------------------------------------------------------------
@@ -68,6 +70,12 @@ namespace Sarand.BusinessMgr.Net.Core.Web.Portal.App
return false;
}
//-------------------------------------------------------------------
// Database context.
/// <summary>Database context specified on sign-in.</summary>
public String DbIdentifier { get; set; }
//-------------------------------------------------------------------
/// <summary>
/// User Profile.
@@ -98,11 +106,22 @@ namespace Sarand.BusinessMgr.Net.Core.Web.Portal.App
}
public FilterData Filter { get; } = new FilterData();
}
/// <summary>
/// Contacts filter properties.
/// </summary>
/// <summary>Contacts filter properties.</summary>
public ContactsData Contacts { get; } = new ContactsData();
// Associations filter.
public class AssociationsData
{
public class FilterData
{
public uint PageNo { get; set; } = 1;
public ushort PageLength { get; set; } = 16;
}
public FilterData Filter { get; } = new FilterData();
}
/// <summary>Associations filter properties.</summary>
public AssociationsData Associations { get; } = new AssociationsData();
// Commodities filter.
public class CommoditiesData
{
@@ -113,9 +132,7 @@ namespace Sarand.BusinessMgr.Net.Core.Web.Portal.App
}
public FilterData Filter { get; } = new FilterData();
}
/// <summary>
/// Commodities filter properties.
/// </summary>
/// <summary>Commodities filter properties.</summary>
public CommoditiesData Commodities { get; } = new CommoditiesData();
}
public UserProfileData Profile { get; protected set; } = new UserProfileData();
@@ -0,0 +1,400 @@
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.Net.Standard.Base.App;
using Sarand.BusinessMgr.Net.Standard.Base.Bus;
using Sarand.BusinessMgr.Net.Standard.Base.Consoles;
using Sarand.BusinessMgr.Net.Standard.Base.Helpers;
using Sarand.BusinessMgr.Net.Standard.Base.Mgr;
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.Email;
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.Collections.Specialized;
using System.Linq;
using System.Threading.Tasks;
using System.Web;
namespace Sarand.BusinessMgr.Net.Core.Web.Portal.Controllers
{
//=======================================================================
// Portal Association Controller - Ajax actions.
//=======================================================================
partial class AssociationController
{
//-------------------------------------------------------------------
// Association data.
private class AssociationData_List : AjaxResponse_Data.ItemData_Base
{
public String OwnerId { get; set; }
public String OwnerName { get; set; }
public String TargetId { get; set; }
public String TargetName { get; set; }
public String TypeCode { get; set; }
public String TypeDesc { get; set; }
public String Code { get; set; }
public String ReferenceExt { get; set; }
public String Group { get; set; }
public String DescDetail { get; set; }
// Address.
public String AddressSummary { get; set; }
}
private class AssociationData_Full : AssociationData_List
{
// Association properties.
public String Location { get; set; }
public String Role { get; set; }
public String PhoneMain { get; set; }
public String Email { get; set; }
public String NominalCodeDefault { get; set; }
}
private void PopulateAssociationResponseData(SbmAssociation association, AssociationData_List data)
{
data.Id = SysId.ToString(association.Id);
data.TypeCode = Internals.AssociationTypeCodes.GetCodeForType(association.AssociationType);
data.TypeDesc = Internals.AssociationTypeCodes.GetDescForType(association.AssociationType);
data.Code = association.Code;
data.ReferenceExt = association.RefExt;
data.Group = association.Group;
data.DescDetail = association.DescDetail;
// Common properties.
data.Notes = association.Notes;
data.StatusCode = association.StatusCode;
data.StatusDesc = association.StatusDesc;
}
private void PopulateAssociationResponseData(SbmAssociation association, AssociationData_Full data)
{
PopulateAssociationResponseData(association, data as AssociationData_List);
// Association properties.
data.Location = association.Location;
data.Role = association.Role;
data.PhoneMain = association.PhoneMain;
data.Email = association.Email;
data.NominalCodeDefault = association.DefaultNominalCode;
}
//-------------------------------------------------------------------
// Association list.
//-------------------------------------------------------------------
private class AjaxResponse_AssociationList : AjaxResponse_Data
{
// Construction.
public AjaxResponse_AssociationList(SbmModel_Portal model) : base(model) { }
// Association list.
public List<AssociationData_List> Associations { get; } = new List<AssociationData_List>();
// Reference data.
public List<CodeDesc> Statuses { get; } = new List<CodeDesc>();
public String DefaultStatusCode { get; set; }
}
// Ajax request.
[HttpPost]
[Route("Association/Ajax/List")]
[Route("Association/Ajax/GetList")]
public IActionResult Ajax_AssociationList(Model_Association model)
{
String fn = GetType().Name + ".Ajax_AssociationList";
AjaxResponse_AssociationList responseData = new AjaxResponse_AssociationList(model);
String messageTitle = Localiser["association.caption.associations"];
// Validate session and referrer.
if (!ValidUserSession) return GetAjaxResult_InvalidSession(responseData);
if (!ValidReferer) return Forbid();
RefreshModel(model);
// Reference data - statuses.
foreach (SaStatus status in SbmAssociation.ValidStatuses.FindAll(item => item != SaStatus.Deleted))
responseData.Statuses.Add(SacsSystem.GetStatusCode(status), Localiser.GetStatusCaption(status));
responseData.DefaultStatusCode = SacsSystem.GetStatusCode(SbmAssociation.DefaultStatus);
// Item filter.
SbmAssociation.Params prmAssociation = new SbmAssociation.Params();
prmAssociation.AffiliateIdOwner = SessionData.Organisation.AffiliateId;
prmAssociation.Search = model.Filter.AssociationSearch;
prmAssociation.AssociationType = model.GetFilterAssociationType();
prmAssociation.StatusCat = SbmAssociation.StatusCat.NotDeleted;
prmAssociation.SortColumns.Add(AccountMgr.GetAssociationColumnDfn(SbmAssociation.Column.Target_Name));
model.Paging.ItemTotal = (ulong)AccountMgr.GetAssociationCount(prmAssociation);
model.PopulatePagingTo(prmAssociation.ResultsPaging);
// Item columns.
ColumnDfn colId = AccountMgr.GetAssociationColumnDfn(SbmAssociation.Column.Id);
ColumnDfn colAffIdTarget = AccountMgr.GetAssociationColumnDfn(SbmAssociation.Column.AffIdTarget);
ColumnDfn colTargetName = AccountMgr.GetAssociationColumnDfn(SbmAssociation.Column.Target_Name);
ColumnDfn colAssociationType = AccountMgr.GetAssociationColumnDfn(SbmAssociation.Column.Type_Desc);
ColumnDfn colCode = AccountMgr.GetAssociationColumnDfn(SbmAssociation.Column.Code);
ColumnDfn colExtRef = AccountMgr.GetAssociationColumnDfn(SbmAssociation.Column.ExtRef);
ColumnDfn colGroup = AccountMgr.GetAssociationColumnDfn(SbmAssociation.Column.Group);
ColumnDfn colDescDetail = AccountMgr.GetAssociationColumnDfn(SbmAssociation.Column.DescDetail);
ColumnDfn colNotes = AccountMgr.GetAssociationColumnDfn(SbmAssociation.Column.Notes);
ColumnDfn colStatus = AccountMgr.GetAssociationColumnDfn(SbmAssociation.Column.Status);
ColumnDfn colCreatedOn = AccountMgr.GetAssociationColumnDfn(SbmAssociation.Column.CreateOn_Date);
ColumnDfn colUpdatedOn = AccountMgr.GetAssociationColumnDfn(SbmAssociation.Column.UpdateOn_Date);
List<ColumnDfn> columns = new List<ColumnDfn>()
{
colId, colAffIdTarget, colTargetName, colAssociationType,
colCode, colExtRef, colGroup, colDescDetail,
colNotes, colStatus, colCreatedOn, colUpdatedOn
};
// Query items.
using (DbResultSet resultSet = AccountMgr.ExecuteAssociationQuery(columns, prmAssociation))
{
while (resultSet != null && resultSet.HasRows && resultSet.NextRecord())
{
NamedValue.Map values = resultSet.GetRecordValues();
Sbm.AssociationType associationType = (Sbm.AssociationType)values.GetLong(colAssociationType.DataName);
AssociationData_List associationData = new AssociationData_List()
{
Id = values.GetString(colId.DataName),
TargetId = values.GetString(colAffIdTarget.DataName),
TargetName = values.GetString(colTargetName.DataName),
TypeCode = Internals.AssociationTypeCodes.GetCodeForType(associationType),
TypeDesc = Internals.AssociationTypeCodes.GetDescForType(associationType),
Code = values.GetString(colCode.DataName),
ReferenceExt = values.GetString(colExtRef.DataName),
Group = values.GetString(colGroup.DataName),
DescDetail = values.GetString(colDescDetail.DataName),
Notes = values.GetString(colNotes.DataName),
StatusCode = values.GetString(colStatus.DataName),
StatusDesc = SacsSystem.GetStatusDescForCode(values.GetString(colStatus.DataName)),
CreatedOn = DateUtil.Format(values.GetDate(colCreatedOn.DataName), "yyyyMMdd"),
CreatedOnFmt = Localiser.FormatDate(values.GetDate(colCreatedOn.DataName)),
UpdatedOn = DateUtil.Format(values.GetDate(colUpdatedOn.DataName), "yyyyMMdd"),
UpdatedOnFmt = Localiser.FormatDate(values.GetDate(colUpdatedOn.DataName))
};
responseData.Associations.Add(associationData);
}
}
// Refresh paging info.
if (model.Paging.IsPaged)
model.Paging.ItemCount = (ushort)responseData.Associations.Count;
SessionData_Portal.Profile.Associations.Filter.PageNo = model.Paging.PageNo;
SessionData_Portal.Profile.Associations.Filter.PageLength = model.Paging.PageLength;
responseData.SetPaging(model);
// Complete response.
responseData.Success = true;
return Json(responseData);
}
//-------------------------------------------------------------------
// Get Association ajax request.
//-------------------------------------------------------------------
private class AjaxResponse_Association : AjaxResponse_Data
{
// Construction.
public AjaxResponse_Association(SbmModel_Portal model) : base(model) { }
// Association details.
public AssociationData_Full association { get; } = new AssociationData_Full();
}
//-------------------------------------------------------------------
[HttpPost]
[Route("Association/Ajax/Item")]
[Route("Association/Ajax/GetItem")]
public IActionResult Ajax_GetItem(Model_Association model)
{
String fn = GetType().Name + ".Ajax_GetItem";
String messageTitle = Localiser["association.caption.association"];
AjaxResponse_Association responseData = new AjaxResponse_Association(model);
// Validate session and referrer.
if (!ValidUserSession) return GetAjaxResult_InvalidSession(responseData);
if (!ValidReferer) return Forbid();
RefreshModel(model);
// Identify item.
SysId associationId = model.AssociationId.ToSysId();
if (!SysId.IsValid(associationId))
return Json(responseData.SetMessage(messageTitle, Localiser["msg.error.item.id.not.specified"]));
// Retrieve item.
SbmAssociation association = AccountMgr.GetAssociationById(associationId);
if (association == null)
{
SaLogger.Warn(fn + " Failed to retrieve association for Id: '" + model.AssociationId + "'");
return Json(responseData.SetMessage(messageTitle, Localiser["msg.error.item.id.not.found"]));
}
// Construct response.
PopulateAssociationResponseData(association, responseData.association);
// Complete response.
responseData.Success = true;
return Json(responseData);
}
//-------------------------------------------------------------------
// Store Association ajax request.
//-------------------------------------------------------------------
[HttpPost]
[Route("Association/Ajax/Store")]
[Route("Association/Ajax/StoreItem")]
public IActionResult Ajax_StoreItem(Model_Association model)
{
String fn = GetType().Name + ".Ajax_StoreItem";
String messageTitle = Localiser["association.caption.association"];
AjaxResponse_Association responseData = new AjaxResponse_Association(model);
// Validate session and referrer.
if (!ValidUserSession) return GetAjaxResult_InvalidSession(responseData);
if (!ValidReferer) return Forbid();
RefreshModel(model);
// Validate item properties.
model.ValidateAssociationData(responseData);
if (responseData.HasControlErrors())
return Json(responseData);
// Validate item Id against 'Exists' flag.
SysId associationId = model.AssociationId.ToSysId();
bool associationExists = StringUtil.ToBool(model.AssociationExists);
if (associationExists && !SysId.IsValid(associationId))
{
SaLogger.Warn(fn + " Id not specified for existing Association.");
return Json(responseData.SetGoHome(messageTitle, Localiser["msg.error.system"]));
}
if (!associationExists && SysId.IsValid(associationId))
{
SaLogger.Warn(fn + " Id specified for new Association: '" + model.AssociationId + "'");
return Json(responseData.SetGoHome(messageTitle, Localiser["msg.error.system"]));
}
// Retrieve existing item.
SbmAssociation association = AccountMgr.GetAssociationById(associationId);
if (association == null && SysId.IsValid(associationId))
{
SaLogger.Warn(fn + " Failed to retrieve association for Id: '" + model.AssociationId + "'");
return Json(responseData.SetMessage(messageTitle, Localiser["msg.error.item.id.not.found"]));
}
// Create new item if required.
if (association == null)
{
association = AccountMgr.CreateAssociation(SessionData.Organisation.AffiliateId, model.GetAssociationType(), model.AffiliateIdTarget.ToSysId());
}
// Populate item properties.
if (model.AssociationCode != null)
association.Code = model.AssociationCode;
if (model.AssociationReferenceExt != null)
association.RefExt = model.AssociationReferenceExt;
if (model.AssociationLocation != null)
association.Location = model.AssociationLocation;
if (model.AssociationRole != null)
association.Role = model.AssociationRole;
if (model.AssociationPhoneMain != null)
association.PhoneMain = model.AssociationPhoneMain;
if (model.AssociationEmail != null)
association.Email = model.AssociationEmail;
if (model.AssociationGroup != null)
association.Group = model.AssociationGroup;
if (model.AssociationDescDetail != null)
association.DescDetail = model.AssociationDescDetail;
if (model.AssociationNominalCodeDefault != null)
association.DefaultNominalCode = model.AssociationNominalCodeDefault;
// Account.
if (model.AssociationAccountManagerCode != null)
association.AccountManagerCode = model.AssociationAccountManagerCode;
if (model.AssociationAccountHandlerCode != null)
association.AccountHandlerCode = model.AssociationAccountHandlerCode;
if (model.AssociationAccountTypeCode != null)
association.AccountTypeCode = model.AssociationAccountTypeCode;
if (model.AssociationAccountRatingCode != null)
association.AccountRatingCode = model.AssociationAccountRatingCode;
// Contact.
if (model.AssociationContactAreaCode != null)
association.ContactAreaCode = model.AssociationContactAreaCode;
if (model.AssociationContactHandlerCode != null)
association.ContactHandlerCode = model.AssociationContactHandlerCode;
if (model.AssociationContactMethodCode != null)
association.ContactMethodCode = model.AssociationContactMethodCode;
if (model.AssociationContactCallCycleCode != null)
association.ContactCallCycleCode = model.AssociationContactCallCycleCode;
if (model.AssociationCallsPerYear != null)
association.CallsPerYear = StringUtil.ToInt(model.AssociationCallsPerYear);
if (model.AssociationLastCallDate != null)
association.LastCall = model.ParseDateFromForm(model.AssociationLastCallDate);
// Customer.
if (model.AssociationCommodityIdSalesDefault != null)
association.CommodityIdSalesDefault = model.AssociationCommodityIdSalesDefault.ToSysId();
if (model.AssociationOrderLeadTimeCode != null)
association.OrderLeadTimeCode = model.AssociationOrderLeadTimeCode;
if (model.AssociationDeliveryTypeCode != null)
association.DeliveryTypeCode = model.AssociationDeliveryTypeCode;
if (model.AssociationDeliveryMethodCode != null)
association.DeliveryMethodCode = model.AssociationDeliveryMethodCode;
if (model.AssociationDeliveryCourierCode != null)
association.DeliveryCourierCode = model.AssociationDeliveryCourierCode;
if (model.AssociationDeliveryBookInCode != null)
association.DeliveryBookInCode = model.AssociationDeliveryBookInCode;
// Transactions.
if (model.AssociationAmountTransMax != null)
association.AmountTransMax = StringUtil.ToDecimal(model.AssociationAmountTransMax);
if (model.AssociationAnnualSalesTarget != null)
association.AnnualSalesTarget = StringUtil.ToDecimal(model.AssociationAnnualSalesTarget);
if (model.AssociationCreditLimit != null)
association.CreditLimit = StringUtil.ToDecimal(model.AssociationCreditLimit);
if (model.AssociationAccountBalance != null)
association.AccountBalance = StringUtil.ToDecimal(model.AssociationAccountBalance);
if (model.AssociationDiscountType != null)
association.DiscountType = model.GetAssociationDiscountType();
if (model.AssociationDiscountRate != null)
association.DiscountRate = StringUtil.ToDecimal(model.AssociationDiscountRate);
if (model.AssociationPriceSetId != null)
association.PriceSetId = model.AssociationPriceSetId.ToSysId();
if (model.AssociationTaxCodeId != null)
association.TaxCodeId = model.AssociationTaxCodeId.ToSysId();
if (model.AssociationPaymentTerms != null)
association.PaymentTerms = StringUtil.ToShort(model.AssociationPaymentTerms);
if (model.AssociationInvoiceDesc != null)
association.InvoiceDesc = model.AssociationInvoiceDesc;
if (model.AssociationTerminatedDate != null)
association.Terminated = model.ParseDateFromForm(model.AssociationTerminatedDate);
// Common properties.
if (model.AssociationNotes != null)
association.Notes = model.AssociationNotes;
if (model.AssociationStatusCode != null)
association.Status = model.AssociationStatus;
// Store item.
if (!SysId.IsValid(association.Id))
{
if (!AccountMgr.InsertAssociation(association, DbUpdateInfo))
{
SaLogger.Warn(fn + " Failed to insert association: " + AccountMgr.ErrorStateMessage);
return Json(responseData.SetMessage(messageTitle, Localiser["msg.error.item.insert.failed"] + "\n\n" + AccountMgr.ErrorStateMessage));
}
responseData.SetNotification(messageTitle, Localiser["msg.item.created"]);
}
else
{
if (!AccountMgr.UpdateAssociation(association, DbUpdateInfo))
{
SaLogger.Warn(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));
}
responseData.SetNotification(messageTitle, Localiser["msg.item.updated"]);
}
// Construct response.
PopulateAssociationResponseData(association, responseData.association);
// Complete response.
responseData.Success = true;
return Json(responseData);
}
//-------------------------------------------------------------------
}
//=======================================================================
}
@@ -0,0 +1,53 @@
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.Net.Standard.Base.App;
using Sarand.BusinessMgr.Net.Standard.Base.Bus;
using Sarand.BusinessMgr.Net.Standard.Base.Mgr;
using Sarand.Net.Standard.Base.App;
using Sarand.Net.Standard.Base.Bus;
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;
namespace Sarand.BusinessMgr.Net.Core.Web.Portal.Controllers
{
//=======================================================================
public partial class AssociationController : SbmController_Portal
{
//-------------------------------------------------------------------
// Data members.
//-------------------------------------------------------------------
// Construction.
public AssociationController(SbmLocaliser localiser, ILogger<AssociationController> logger) :
base(localiser, logger)
{
}
//-------------------------------------------------------------------
[HttpGet]
public IActionResult Index()
{
String fn = GetType().Name + ".Index";
if (!ValidUserSession)
return RedirectToAction("", "");
// Association page.
Model_Association model = new Model_Association(Localiser);
model.Paging.PageNo = SessionData_Portal.Profile.Associations.Filter.PageNo;
model.Paging.PageLength = SessionData_Portal.Profile.Associations.Filter.PageLength;
return View(model);
}
//-------------------------------------------------------------------
}
//=======================================================================
}
@@ -31,7 +31,7 @@ namespace Sarand.BusinessMgr.Net.Core.Web.Portal.Controllers
{
//-------------------------------------------------------------------
// Commodity data.
private class CommodityData_List : AjaxResponse_Data.ItemData
private class CommodityData_List : AjaxResponse_Data.ItemData_Base
{
public String TypeCode { get; set; }
public String TypeDesc { get; set; }
@@ -31,7 +31,7 @@ namespace Sarand.BusinessMgr.Net.Core.Web.Portal.Controllers
{
//-------------------------------------------------------------------
// Affiliate data.
private class AffiliateData_List : AjaxResponse_Data.ItemData
private class AffiliateData_List : AjaxResponse_Data.ItemData_Base
{
public String DisplayName { get; set; }
public String TypeCode { get; set; }
@@ -18,103 +18,102 @@ using System.Threading.Tasks;
namespace Sarand.BusinessMgr.Net.Core.Web.Portal.Controllers
{
//=======================================================================
public class HomeController : SbmController_Portal
{
//-------------------------------------------------------------------
// Data members.
//=======================================================================
public class HomeController : SbmController_Portal
{
//-------------------------------------------------------------------
// Data members.
//-------------------------------------------------------------------
// Construction.
public HomeController(SbmLocaliser localiser, ILogger<HomeController> logger) :
base(localiser, logger)
{
}
//-------------------------------------------------------------------
// Construction.
public HomeController(SbmLocaliser localiser, ILogger<HomeController> logger) :
base(localiser, logger)
{
}
//-------------------------------------------------------------------
[HttpGet]
public IActionResult Index()
{
String fn = GetType().Name + ".Index";
//-------------------------------------------------------------------
[HttpGet]
public IActionResult Index()
{
// 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 = 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.GetSessionContextIdxsForUsersBusMgrDatabase(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, 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(fn + " Keep Logged In cookie found but with incomplete details." + logSuffix);
}
// 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);
model.ProhibitCache = false;
return View(model);
}
// Home page.
Model_Home model = new Model_Home(Localiser);
model.ProhibitCache = false;
return View(model);
}
//-------------------------------------------------------------------
[ResponseCache(Duration = 0, Location = ResponseCacheLocation.None, NoStore = true)]
public IActionResult Error()
{
return View(new ErrorViewModel(Localiser) { RequestId = Activity.Current?.Id ?? HttpContext.TraceIdentifier });
}
//-------------------------------------------------------------------
[ResponseCache(Duration = 0, Location = ResponseCacheLocation.None, NoStore = true)]
public IActionResult Error()
{
return View(new ErrorViewModel(Localiser) { RequestId = Activity.Current?.Id ?? HttpContext.TraceIdentifier });
}
//-------------------------------------------------------------------
}
//=======================================================================
//-------------------------------------------------------------------
}
//=======================================================================
}
@@ -18,6 +18,7 @@ using System.Collections.Specialized;
using System.Linq;
using System.Threading.Tasks;
using System.Web;
using static Sarand.BusinessMgr.Net.Standard.Base.App.Sbm.Config.Database;
namespace Sarand.BusinessMgr.Net.Core.Web.Portal.Controllers
{
@@ -52,7 +53,7 @@ namespace Sarand.BusinessMgr.Net.Core.Web.Portal.Controllers
/// <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, SaSecurityItem securityItemUser, SbmAffiliate affiliateUser, List<SysIdString> supervisedOrganisations, SbmAssociation associationOrganisation, SbmAffiliate affiliateOrganisation, bool keepLoggedIn, ref String errorMessage)
protected bool SignUserIn(ISbmUserContext userContext, String dbIdentifier, SaSecurityItem securityItemUser, SbmAffiliate affiliateUser, List<SysIdString> supervisedOrganisations, SbmAssociation associationOrganisation, SbmAffiliate affiliateOrganisation, bool keepLoggedIn, ref String errorMessage)
{
String fn = GetType().Name + ".SignUserIn";
@@ -125,7 +126,7 @@ namespace Sarand.BusinessMgr.Net.Core.Web.Portal.Controllers
}
// Create session.
OpenUserSession(userContext.SessionContext, securityItemUser, affiliateUser, supervisedOrganisations, associationOrganisation, affiliateOrganisation);
OpenUserSession(userContext.SessionContext, dbIdentifier, securityItemUser, affiliateUser, supervisedOrganisations, associationOrganisation, affiliateOrganisation);
// Send the 'Keep Logged In' cookie if requested.
if (affiliateUser.KeepLoggedIn_Portal)
@@ -153,7 +154,7 @@ namespace Sarand.BusinessMgr.Net.Core.Web.Portal.Controllers
//-------------------------------------------------------------------
/// <summary>Start a new user session on sign-in.</summary>
protected bool OpenUserSession(SbmSessionContext sessionContext, SaSecurityItem securityItemUser, SbmAffiliate affiliateUser, List<SysIdString> supervisedOrganisations, SbmAssociation associationOrganisation, SbmAffiliate affiliateOrganisation)
protected bool OpenUserSession(SbmSessionContext sessionContext, String dbIdentifier, SaSecurityItem securityItemUser, SbmAffiliate affiliateUser, List<SysIdString> supervisedOrganisations, SbmAssociation associationOrganisation, SbmAffiliate affiliateOrganisation)
{
// Validate user properties.
if (!SysId.IsValid(affiliateUser.Id)) return false;
@@ -163,7 +164,7 @@ namespace Sarand.BusinessMgr.Net.Core.Web.Portal.Controllers
CloseUserSession();
// Open session.
return SetSessionData(new SbmSessionData_Portal(sessionContext, securityItemUser, affiliateUser, supervisedOrganisations, associationOrganisation, affiliateOrganisation));
return SetSessionData(new SbmSessionData_Portal(sessionContext, dbIdentifier, securityItemUser, affiliateUser, supervisedOrganisations, associationOrganisation, affiliateOrganisation));
}
protected void CloseUserSession()
{
@@ -205,6 +206,7 @@ namespace Sarand.BusinessMgr.Net.Core.Web.Portal.Controllers
//-------------------------------------------------------------------
// Keep-logged-in cookie value.
private static readonly String ParamName_KeepLoggedIn_DbIdentifier = "dbi";
private static readonly String ParamName_KeepLoggedIn_EmailAddress = "ema";
private static readonly String ParamName_KeepLoggedIn_SecurityItemId = "sii";
private static readonly String ParamName_KeepLoggedIn_AffiliateIdUser = "afiu";
@@ -212,6 +214,7 @@ namespace Sarand.BusinessMgr.Net.Core.Web.Portal.Controllers
private String GenerateKeepLoggedInCookieValue()
{
NameValueCollection keepLoggedInParams = new NameValueCollection();
keepLoggedInParams.Add(ParamName_KeepLoggedIn_DbIdentifier, SessionData_Portal.DbIdentifier);
keepLoggedInParams.Add(ParamName_KeepLoggedIn_EmailAddress, SessionData_Portal.User.EmailAddress);
keepLoggedInParams.Add(ParamName_KeepLoggedIn_SecurityItemId, SysId.ToString(SessionData_Portal.User.SecurityItemId));
keepLoggedInParams.Add(ParamName_KeepLoggedIn_AffiliateIdUser, SysId.ToString(SessionData_Portal.User.AffiliateId));
@@ -222,6 +225,7 @@ namespace Sarand.BusinessMgr.Net.Core.Web.Portal.Controllers
//-------------------------------------------------------------------
public class KeepLoggedInParams
{
public String DbIdentifier { get; set; }
public String EmailAddress { get; set; }
public SysId SecurityItemId { get; set; }
public SysId AffiliateIdUser { get; set; }
@@ -230,7 +234,9 @@ namespace Sarand.BusinessMgr.Net.Core.Web.Portal.Controllers
public List<String> GetValidParams()
{
List<String> values = new List<String>();
if (!StringUtil.IsEmpty(EmailAddress))
if (!StringUtil.IsBlank(DbIdentifier))
values.Add("DbIdentifier: " + DbIdentifier);
if (!StringUtil.IsBlank(EmailAddress))
values.Add("EmailAddress: " + EmailAddress);
if (SysId.IsValid(SecurityItemId))
values.Add("SecurityItemId: " + SysId.ToString(SecurityItemId));
@@ -244,7 +250,8 @@ namespace Sarand.BusinessMgr.Net.Core.Web.Portal.Controllers
public bool IsValid()
{
return
!StringUtil.IsEmpty(EmailAddress) &&
!StringUtil.IsBlank(DbIdentifier) &&
StringUtil.IsBlank(EmailAddress) &&
SysId.IsValid(SecurityItemId) &&
SysId.IsValid(AffiliateIdUser) &&
SysId.IsValid(AssociationIdOrganisation);
@@ -256,6 +263,7 @@ namespace Sarand.BusinessMgr.Net.Core.Web.Portal.Controllers
if (!StringUtil.IsEmpty(encryptedParams))
{
NameValueCollection linkParams = DecryptParameters(encryptedParams, Base64IV_KeepLoggedIn, Base64Key_KeepLoggedIn);
keepLoggedInParams.DbIdentifier = linkParams.Get(ParamName_KeepLoggedIn_DbIdentifier);
keepLoggedInParams.EmailAddress = linkParams.Get(ParamName_KeepLoggedIn_EmailAddress);
keepLoggedInParams.SecurityItemId = linkParams.Get(ParamName_KeepLoggedIn_SecurityItemId).ToSysId();
keepLoggedInParams.AffiliateIdUser = linkParams.Get(ParamName_KeepLoggedIn_AffiliateIdUser).ToSysId();
@@ -54,16 +54,20 @@ namespace Sarand.BusinessMgr.Net.Core.Web.Portal.Controllers
model.ValidateSignInData(responseData);
if (responseData.HasControlErrors())
return Json(responseData);
// Identify session contexts with a BusMgr database that hosts the specified email address.
List<ushort> sessionContextIdxs = SbmSessionContextHelper_Web.GetSessionContextIdxsForUsersBusMgrDatabase(model.EmailAddress);
foreach (ushort sessionContextIdx in sessionContextIdxs)
using (DisposableCollection<IDisposable> disposableItems = new DisposableCollection<IDisposable>())
{
SbmSessionContext sessionContext = SbmSessionContextHelper_Web.GetSessionContext(sessionContextIdx);
SbmUserContext_Web userContext = new SbmUserContext_Web(sessionContext);
using (DisposableCollection<IDisposable> disposableItems = 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)
{
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);
@@ -73,27 +77,36 @@ namespace Sarand.BusinessMgr.Net.Core.Web.Portal.Controllers
// User's security item.
SaSecurityItem securityItemUser = GetSecurityItemForSignin(securityConsoleForUser, model);
if (securityItemUser == null)
return Json(responseData.SetControlError(nameof(model.EmailAddress), model.Localiser["security.msg.signin.invalid"]));
{
SaLogger.Warn(fn + " Failed to retrieve Security Item. Database: " + busMgrDatabase.DatabaseName + ". Email: " + model.EmailAddress);
continue;
}
disposableItems.Add(securityItemUser);
// User's affiliate.
SbmAffiliate affiliateUser = GetAffiliateForSignin(accountMgrForUser, model, securityItemUser);
if (affiliateUser == null)
return Json(responseData.SetControlError(nameof(model.EmailAddress), model.Localiser["security.msg.signin.invalid"]));
{
SaLogger.Warn(fn + " Failed to retrieve Affiliate for signin. Database: " + busMgrDatabase.DatabaseName + ". UserId: " + SysId.ToString(securityItemUser.Id));
continue;
}
disposableItems.Add(affiliateUser);
// User's supervised organisations.
List<SysIdString> supervisedOrganisations = accountMgrForUser.GetAffiliatesSupervisedBy(affiliateUser.Id);
if (supervisedOrganisations.IsEmpty())
{
SaLogger.Warn(fn + " User has no supervised organisations. Email: " + model.EmailAddress);
return Json(responseData.SetControlError(nameof(model.EmailAddress), model.Localiser["security.msg.signin.invalid"]));
SaLogger.Warn(fn + " User has no supervised organisations. Database: " + busMgrDatabase.DatabaseName + ". Email: " + model.EmailAddress);
continue;
}
// User's organisation association.
SbmAssociation associationOrganisation = GetOrganisationAssociationForSignIn(accountMgrForUser, model, affiliateUser, supervisedOrganisations);
if (associationOrganisation == null)
return Json(responseData.SetControlError(nameof(model.EmailAddress), model.Localiser["security.msg.signin.invalid"]));
{
SaLogger.Warn(fn + " Failed to identify Organisation Association. Database: " + busMgrDatabase.DatabaseName + ". Email: " + model.EmailAddress);
continue;
}
disposableItems.Add(associationOrganisation);
// User's organisation affiliate.
@@ -101,15 +114,22 @@ namespace Sarand.BusinessMgr.Net.Core.Web.Portal.Controllers
disposableItems.Add(affiliateOrganisation);
if (affiliateOrganisation == null)
{
SaLogger.Warn(fn + " Failed to retrieve Affiliate for organisation. Id: " + SysId.ToString(associationOrganisation?.Id ?? null));
return Json(responseData.SetControlError(nameof(model.EmailAddress), model.Localiser["security.msg.signin.invalid"]));
SaLogger.Warn(fn + " Failed to retrieve Affiliate for organisation. Database: " + busMgrDatabase.DatabaseName + ". Id: " + SysId.ToString(associationOrganisation?.Id ?? null));
continue;
}
// Initialise user session.
String errorMessage = "";
if (!SignUserIn(userContext, securityItemUser, affiliateUser, supervisedOrganisations, associationOrganisation, affiliateOrganisation, model.KeepMeSignedIn, ref errorMessage))
return Json(responseData.SetControlError(nameof(model.EmailAddress), 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 (!(SessionData_Portal?.ValidUserSession ?? false))
{
SaLogger.Warn(fn + " Failed to identify Suitable database. Email: " + model.EmailAddress);
return Json(responseData.SetControlError(nameof(model.EmailAddress), model.Localiser["security.msg.signin.invalid"]));
}
// Construct response.
responseData.Success = SessionData_Portal?.ValidUserSession ?? false;
@@ -131,7 +151,7 @@ namespace Sarand.BusinessMgr.Net.Core.Web.Portal.Controllers
}
// Validate password.
if (!SbmPasswordHelper.VerifyHashedPassword(securityItemUser, model.Password) && !(DebugMode && model.EmailAddress.EqualsCI("ahoward@sarand.com")))
if (!SbmPasswordHelper.VerifyHashedPassword(securityItemUser, model.Password) && !(DebugMode && model.EmailAddress.EqualsCI("andy@sarand.com")))
{
// Record invalid login attempt.
securityItemUser.InvalidLoginCount++;
@@ -331,8 +351,8 @@ namespace Sarand.BusinessMgr.Net.Core.Web.Portal.Controllers
if (requestPasswordParams.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.GetSessionContextIdxsForUsersBusMgrDatabase(requestPasswordParams.EmailAddress);
// Identify session contexts for databases with specified Id that hosts the specified email address.
List<ushort> sessionContextIdxs = SbmSessionContextHelper_Web.GetSessionContextIdxsForContextUserEmail(requestPasswordParams.DbIdentifier, requestPasswordParams.EmailAddress);
foreach (ushort sessionContextIdx in sessionContextIdxs)
{
// Consoles and Managers must be associated with the specific context.
@@ -460,7 +480,7 @@ namespace Sarand.BusinessMgr.Net.Core.Web.Portal.Controllers
if (responseData.HasControlErrors()) return Json(responseData);
// Identify session contexts with a BusMgr database that hosts the specified email address.
List<ushort> sessionContextIdxs = SbmSessionContextHelper_Web.GetSessionContextIdxsForUsersBusMgrDatabase(model.EmailAddress);
List<ushort> sessionContextIdxs = SbmSessionContextHelper_Web.GetSessionContextIdxsForContextUserEmail(model.DbIdentifier, model.EmailAddress);
foreach (ushort sessionContextIdx in sessionContextIdxs)
{
SbmSessionContext sessionContext = SbmSessionContextHelper_Web.GetSessionContext(sessionContextIdx);
@@ -519,13 +539,14 @@ namespace Sarand.BusinessMgr.Net.Core.Web.Portal.Controllers
emailConsole.ReplaceDynamicEmailBodyTags(emailData, null);
// Replace dynamic tag for link back to change password page.
SaEmailConsole.ReplaceDynamicEmailTag(emailData, "[#Link.RequestPassword]", GenerateRequestPasswordLink(securityItemUser.Id, affiliateUser.Id, securityItemUser.EmailAddress, ""));
SaEmailConsole.ReplaceDynamicEmailTag(emailData, "[#Link.RequestPassword]", GenerateRequestPasswordLink(SessionData_Portal.DbIdentifier, securityItemUser.Id, affiliateUser.Id, securityItemUser.EmailAddress, ""));
SaEmailConsole.ReplaceDynamicEmailTag(emailData, "[#Link.ExpiresMinutes]", GetRequestPasswordEmailLinkExpiresMinutes().ToString());
}
//-------------------------------------------------------------------
// Password request link.
private static String ParamName_RequestPasswordEncrypted { get; } = "rpe";
private static String ParamName_DbIdentifier { get; } = "dbi";
private static String ParamName_EmailAddress { get; } = "ema";
private static String ParamName_SecurityItemId { get; } = "sei";
private static String ParamName_AffiliateId { get; } = "afi";
@@ -536,9 +557,10 @@ namespace Sarand.BusinessMgr.Net.Core.Web.Portal.Controllers
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==";
private String GenerateRequestPasswordLink(SysId securityItemId, SysId affiliateId, String emailAddress, String requestPasswordToken)
private String GenerateRequestPasswordLink(String dbIdentifier, SysId securityItemId, SysId affiliateId, String emailAddress, String requestPasswordToken)
{
NameValueCollection parameters = new NameValueCollection();
parameters.Add(ParamName_DbIdentifier, dbIdentifier);
parameters.Add(ParamName_EmailAddress, emailAddress);
parameters.Add(ParamName_SecurityItemId, SysId.ToString(securityItemId));
parameters.Add(ParamName_AffiliateId, SysId.ToString(affiliateId));
@@ -558,6 +580,7 @@ namespace Sarand.BusinessMgr.Net.Core.Web.Portal.Controllers
}
private class RequestPasswordParams
{
public String DbIdentifier { get; set; }
public String EmailAddress { get; set; }
public SysId SecurityItemId { get; set; }
public SysId AffiliateId { get; set; }
@@ -567,7 +590,9 @@ namespace Sarand.BusinessMgr.Net.Core.Web.Portal.Controllers
public List<String> GetValidParams()
{
List<String> values = new List<String>();
if (!StringUtil.IsEmpty(EmailAddress))
if (!StringUtil.IsBlank(DbIdentifier))
values.Add("DbIdentifier: " + DbIdentifier);
if (!StringUtil.IsBlank(EmailAddress))
values.Add("Email: " + EmailAddress);
if (SysId.IsValid(SecurityItemId))
values.Add("SecurityItemId: " + SysId.ToString(SecurityItemId));
@@ -575,7 +600,7 @@ namespace Sarand.BusinessMgr.Net.Core.Web.Portal.Controllers
values.Add("AffiliateId: " + SysId.ToString(AffiliateId));
if (DateUtil.IsValid(ExpiresAt))
values.Add("Expires: " + DateUtil.FormatXmlDateTime(ExpiresAt));
if (!StringUtil.IsEmpty(Token))
if (!StringUtil.IsBlank(Token))
values.Add("Token: " + Token);
return values;
}
@@ -583,7 +608,8 @@ namespace Sarand.BusinessMgr.Net.Core.Web.Portal.Controllers
public bool IsValid()
{
return
!StringUtil.IsEmpty(EmailAddress) &&
!StringUtil.IsBlank(DbIdentifier) &&
StringUtil.IsBlank(EmailAddress) &&
SysId.IsValid(SecurityItemId) &&
SysId.IsValid(AffiliateId) &&
DateUtil.IsValid(ExpiresAt) && ExpiresAt >= DateTime.Now;
@@ -606,6 +632,7 @@ namespace Sarand.BusinessMgr.Net.Core.Web.Portal.Controllers
if (!StringUtil.IsEmpty(encryptedParams))
{
NameValueCollection linkParams = DecryptParameters(encryptedParams, Base64IV_RequestPassword, Base64Key_RequestPassword);
requestPasswordParams.DbIdentifier = linkParams.Get(ParamName_DbIdentifier);
requestPasswordParams.EmailAddress = linkParams.Get(ParamName_EmailAddress);
requestPasswordParams.SecurityItemId = new SysId(StringUtil.ToInt(linkParams.Get(ParamName_SecurityItemId)));
requestPasswordParams.AffiliateId = new SysId(StringUtil.ToInt(linkParams.Get(ParamName_AffiliateId)));
@@ -78,7 +78,7 @@ namespace Sarand.BusinessMgr.Net.Core.Web.Portal.Controllers
String logSuffix = requestPasswordParams.GetValidParams().Concat("", " ", ".");
// 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.GetSessionContextIdxsForUsersBusMgrDatabase(requestPasswordParams.EmailAddress);
List<ushort> sessionContextIdxs = SbmSessionContextHelper_Web.GetSessionContextIdxsForContextUserEmail(requestPasswordParams.DbIdentifier, requestPasswordParams.EmailAddress);
foreach (ushort sessionContextIdx in sessionContextIdxs)
{
// Consoles and Managers must be associated with the specific context.
@@ -0,0 +1,272 @@
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_Association : SbmModel_Portal
{
//-------------------------------------------------------------------
// Construction.
public Model_Association(SbmLocaliser localiser) :
base(localiser)
{
AdditionalFiles_JS.Add("js/sbm-association.js");
}
//-------------------------------------------------------------------
// Filter properties.
public class FilterData
{
public String AssociationSearch { get; set; }
public String AssociationType { get; set; }
}
public FilterData Filter { get; } = new FilterData();
public Sbm.AssociationType GetFilterAssociationType()
{
return (Sbm.AssociationType)Internals.AssociationTypeCodes.GetTypeForCode(Filter.AssociationType);
}
//-------------------------------------------------------------------
// Association properties.
public String AssociationId { get; set; }
public String AssociationExists { get; set; }
public String AffiliateIdOwner { get; set; }
public String AffiliateIdTarget { get; set; }
public String AssociationType { get; set; }
public Sbm.AssociationType GetAssociationType()
{
return (Sbm.AssociationType)Internals.AssociationTypeCodes.GetTypeForCode(AssociationType);
}
public String AssociationCode { get; set; }
public String AssociationReferenceExt { get; set; }
public String AssociationLocation { get; set; }
public String AssociationRole { get; set; }
public String AssociationPhoneMain { get; set; }
public String AssociationEmail { get; set; }
public String AssociationGroup { get; set; }
public String AssociationDescDetail { get; set; }
public String AssociationNominalCodeDefault { get; set; }
// Account.
public String AssociationAccountManagerCode { get; set; }
public String AssociationAccountHandlerCode { get; set; }
public String AssociationAccountTypeCode { get; set; }
public String AssociationAccountRatingCode { get; set; }
// Contact.
public String AssociationContactAreaCode { get; set; }
public String AssociationContactHandlerCode { get; set; }
public String AssociationContactMethodCode { get; set; }
public String AssociationContactCallCycleCode { get; set; }
public String AssociationCallsPerYear { get; set; }
public String AssociationLastCallDate { get; set; }
// Customer.
public String AssociationCommodityIdSalesDefault { get; set; }
public String AssociationOrderLeadTimeCode { get; set; }
public String AssociationDeliveryTypeCode { get; set; }
public String AssociationDeliveryMethodCode { get; set; }
public String AssociationDeliveryCourierCode { get; set; }
public String AssociationDeliveryBookInCode { get; set; }
// Transactions.
public String AssociationAmountTransMax { get; set; }
public String AssociationAnnualSalesTarget { get; set; }
public String AssociationCreditLimit { get; set; }
public String AssociationAccountBalance { get; set; }
public String AssociationDiscountType { get; set; }
public Sbm.DiscountType GetAssociationDiscountType()
{
return Sbm.DiscountType.NoDiscount;
}
public String AssociationDiscountRate { get; set; }
public String AssociationPriceSetId { get; set; }
public String AssociationTaxCodeId { get; set; }
public String AssociationPaymentTerms { get; set; }
public String AssociationInvoiceDesc { get; set; }
public String AssociationTerminatedDate { get; set; }
// Common properties.
public String AssociationNotes { get; set; }
public String AssociationStatusCode { get; set; }
public SaStatus AssociationStatus { get { return SacsSystem.GetStatusForCode(AssociationStatusCode); } }
//-------------------------------------------------------------------
// Validation.
//-------------------------------------------------------------------
// Association validation.
public bool ValidateAssociationData(SbmController_Base.AjaxResponse_Data responseData)
{
//---------------------------------------------------------------
// Association Type.
if (AssociationType != null)
{
if (StringUtil.IsEmpty(AssociationType))
responseData.SetControlError(nameof(AssociationType), Localiser["msg.validation.mandatory.value"]);
}
// Association Code.
if (AssociationCode != null)
{
responseData.SetControlValue(nameof(AssociationCode), AssociationCode);
if (StringUtil.IsEmpty(AssociationCode))
responseData.SetControlError(nameof(AssociationCode), Localiser["msg.validation.mandatory.value"]);
if (StringUtil.GetLength(AssociationCode) > GetControlMaxLength(SbmDbDfn.ColumnIdx.Association_Code))
responseData.SetControlError(nameof(AssociationCode), Localiser["msg.validation.maximum.length", GetControlMaxLength(SbmDbDfn.ColumnIdx.Association_Code)]);
}
// Association External Reference.
if (AssociationReferenceExt != null)
{
responseData.SetControlValue(nameof(AssociationReferenceExt), AssociationReferenceExt);
if (StringUtil.GetLength(AssociationReferenceExt) > GetControlMaxLength(SbmDbDfn.ColumnIdx.Association_ExtRef))
responseData.SetControlError(nameof(AssociationReferenceExt), Localiser["msg.validation.maximum.length", GetControlMaxLength(SbmDbDfn.ColumnIdx.Association_ExtRef)]);
}
// Association Location.
if (AssociationLocation != null)
{
responseData.SetControlValue(nameof(AssociationLocation), AssociationLocation);
if (StringUtil.GetLength(AssociationLocation) > GetControlMaxLength(SbmDbDfn.ColumnIdx.Association_Location))
responseData.SetControlError(nameof(AssociationLocation), Localiser["msg.validation.maximum.length", GetControlMaxLength(SbmDbDfn.ColumnIdx.Association_Location)]);
}
// Association Role.
if (AssociationRole != null)
{
responseData.SetControlValue(nameof(AssociationRole), AssociationRole);
if (StringUtil.GetLength(AssociationRole) > GetControlMaxLength(SbmDbDfn.ColumnIdx.Association_Role))
responseData.SetControlError(nameof(AssociationRole), Localiser["msg.validation.maximum.length", GetControlMaxLength(SbmDbDfn.ColumnIdx.Association_Role)]);
}
// Association Phone Main.
if (AssociationPhoneMain != null)
{
responseData.SetControlValue(nameof(AssociationPhoneMain), AssociationPhoneMain);
if (StringUtil.GetLength(AssociationPhoneMain) > GetControlMaxLength(SbmDbDfn.ColumnIdx.Association_PhoneMain))
responseData.SetControlError(nameof(AssociationPhoneMain), Localiser["msg.validation.maximum.length", GetControlMaxLength(SbmDbDfn.ColumnIdx.Association_PhoneMain)]);
}
// Association Email.
if (AssociationEmail != null)
{
responseData.SetControlValue(nameof(AssociationEmail), AssociationEmail);
if (StringUtil.GetLength(AssociationEmail) > GetControlMaxLength(SbmDbDfn.ColumnIdx.Association_Email))
responseData.SetControlError(nameof(AssociationEmail), Localiser["msg.validation.maximum.length", GetControlMaxLength(SbmDbDfn.ColumnIdx.Association_Email)]);
}
// Association Group.
if (AssociationGroup != null)
{
responseData.SetControlValue(nameof(AssociationGroup), AssociationGroup);
if (StringUtil.GetLength(AssociationGroup) > GetControlMaxLength(SbmDbDfn.ColumnIdx.Association_Group))
responseData.SetControlError(nameof(AssociationGroup), Localiser["msg.validation.maximum.length", GetControlMaxLength(SbmDbDfn.ColumnIdx.Association_Group)]);
}
// Association Description.
if (AssociationDescDetail != null)
{
responseData.SetControlValue(nameof(AssociationDescDetail), AssociationDescDetail);
if (StringUtil.IsEmpty(AssociationDescDetail))
responseData.SetControlError(nameof(AssociationDescDetail), Localiser["msg.validation.mandatory.value"]);
if (StringUtil.GetLength(AssociationDescDetail) > GetControlMaxLength(SbmDbDfn.ColumnIdx.Association_DescDetail))
responseData.SetControlError(nameof(AssociationDescDetail), Localiser["msg.validation.maximum.length", GetControlMaxLength(SbmDbDfn.ColumnIdx.Association_DescDetail)]);
}
// Association Default Nominal Code.
if (AssociationNominalCodeDefault != null)
{
responseData.SetControlValue(nameof(AssociationNominalCodeDefault), AssociationNominalCodeDefault);
if (StringUtil.GetLength(AssociationNominalCodeDefault) > GetControlMaxLength(SbmDbDfn.ColumnIdx.Association_DefaultNominalCode))
responseData.SetControlError(nameof(AssociationNominalCodeDefault), Localiser["msg.validation.maximum.length", GetControlMaxLength(SbmDbDfn.ColumnIdx.Association_DefaultNominalCode)]);
}
//---------------------------------------------------------------
// Account.
// Account Manager code - AssociationAccountManagerCode
// Account Handler code - AssociationAccountHandlerCode
// Account Type code - AssociationAccountTypeCode
// Account Rating code - AssociationAccountRatingCode
//---------------------------------------------------------------
// Contact.
// Contact Area code - AssociationContactAreaCode
// Contact Handler code - AssociationContactHandlerCode
// Contact Method code - AssociationContactMethodCode
// Contact Call Cycle code - AssociationContactCallCycleCode
// Calls per year - AssociationCallsPerYear
// Last Call date - AssociationLastCallDate
//---------------------------------------------------------------
// Customer.
// AssociationCommodityIdSalesDefault
// AssociationOrderLeadTimeCode
// AssociationDeliveryTypeCode
// AssociationDeliveryMethodCode
// AssociationDeliveryCourierCode
// AssociationDeliveryBookInCode
//---------------------------------------------------------------
// Transactions.
// AssociationAmountTransMax
// AssociationAnnualSalesTarget
// AssociationCreditLimit
// AssociationAccountBalance
// AssociationDiscountType
// AssociationDiscountRate
// AssociationPriceSetId
// AssociationTaxCodeId
// AssociationPaymentTerms
// AssociationInvoiceDesc
// AssociationTerminatedDate
// AssociationAssociation
//---------------------------------------------------------------
// Common properties.
// Association Notes.
if (AssociationNotes != null)
{
responseData.SetControlValue(nameof(AssociationNotes), AssociationNotes);
if (!StringUtil.IsEmpty(AssociationNotes) && StringUtil.GetLength(AssociationNotes) > GetControlMaxLength(SbmDbDfn.ColumnIdx.Association_Notes))
responseData.SetControlError(nameof(AssociationNotes), Localiser["msg.validation.maximum.length", GetControlMaxLength(SbmDbDfn.ColumnIdx.Association_Notes)]);
}
// Association status.
if (AssociationStatusCode != null)
{
if (StringUtil.IsEmpty(AssociationStatusCode))
responseData.SetControlError(nameof(AssociationStatusCode), Localiser["msg.validation.mandatory.value"]);
else if (!SbmAssociation.ValidStatuses.Contains(AssociationStatus))
responseData.SetControlError(nameof(AssociationStatusCode), Localiser["msg.validation.invalid.value"]);
}
//---------------------------------------------------------------
return !responseData.HasControlErrors();
}
//-------------------------------------------------------------------
}
//=======================================================================
}
@@ -30,6 +30,8 @@ namespace Sarand.BusinessMgr.Net.Core.Web.Portal.Models
//-------------------------------------------------------------------
// Form properties.
/// <summary>Database config identifier.</summary>
public String DbIdentifier { get; set; }
public String EmailAddress { get; set; }
public String PasswordCurrent { get; set; }
public String Password { get; set; }
@@ -44,17 +46,31 @@ namespace Sarand.BusinessMgr.Net.Core.Web.Portal.Models
// Sign-in.
public bool ValidateSignInData(SbmController_Base.AjaxResponse_Data responseData)
{
// Email address.
responseData.SetControlValue(nameof(EmailAddress), EmailAddress);
if (StringUtil.IsEmpty(EmailAddress))
responseData.SetControlError(nameof(EmailAddress), Localiser["msg.validation.mandatory.value"]);
else if (!SacsDataValidationHelper.IsValidEmailAddress(EmailAddress))
responseData.SetControlError(nameof(EmailAddress), Localiser["msg.validation.invalid.value"]);
{
// Database context.
String controlName = nameof(DbIdentifier);
responseData.SetControlValue(controlName, DbIdentifier);
if (StringUtil.IsEmpty(DbIdentifier))
responseData.SetControlError(controlName, Localiser["msg.validation.mandatory.value"]);
}
// Password.
// NB Do not return the password value in the response.
if (StringUtil.IsEmpty(Password))
responseData.SetControlError(nameof(Password), Localiser["msg.validation.mandatory.value"]);
{
// Email address.
String controlName = nameof(EmailAddress);
responseData.SetControlValue(controlName, EmailAddress);
if (StringUtil.IsEmpty(EmailAddress))
responseData.SetControlError(controlName, Localiser["msg.validation.mandatory.value"]);
else if (!SacsDataValidationHelper.IsValidEmailAddress(EmailAddress))
responseData.SetControlError(controlName, Localiser["msg.validation.invalid.value"]);
}
{
// Password.
// NB Do not return the password value in the response.
String controlName = nameof(Password);
if (StringUtil.IsEmpty(Password))
responseData.SetControlError(controlName, Localiser["msg.validation.mandatory.value"]);
}
return !responseData.HasControlErrors();
}
@@ -96,12 +112,23 @@ namespace Sarand.BusinessMgr.Net.Core.Web.Portal.Models
// Request Password.
public bool ValidateRequestPasswordData(SbmController_Base.AjaxResponse_Data responseData)
{
// Email address.
responseData.SetControlValue(nameof(EmailAddress), EmailAddress);
if (StringUtil.IsEmpty(EmailAddress))
responseData.SetControlError(nameof(EmailAddress), Localiser["msg.validation.mandatory.value"]);
else if (!SacsDataValidationHelper.IsValidEmailAddress(EmailAddress))
responseData.SetControlError(nameof(EmailAddress), Localiser["msg.validation.invalid.value"]);
{
// Database context.
String controlName = nameof(DbIdentifier);
responseData.SetControlValue(controlName, DbIdentifier);
if (StringUtil.IsEmpty(DbIdentifier))
responseData.SetControlError(controlName, Localiser["msg.validation.mandatory.value"]);
}
{
// Email address.
String controlName = nameof(EmailAddress);
responseData.SetControlValue(controlName, EmailAddress);
if (StringUtil.IsEmpty(EmailAddress))
responseData.SetControlError(controlName, Localiser["msg.validation.mandatory.value"]);
else if (!SacsDataValidationHelper.IsValidEmailAddress(EmailAddress))
responseData.SetControlError(controlName, Localiser["msg.validation.invalid.value"]);
}
return !responseData.HasControlErrors();
}
@@ -1,44 +1,43 @@
<Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup>
<TargetFramework>netcoreapp3.1</TargetFramework>
<CopyRefAssembliesToPublishDirectory>false</CopyRefAssembliesToPublishDirectory>
<RootNamespace>Sarand.BusinessMgr.Net.Core.Web</RootNamespace>
<AssemblyName>Sarand.BusinessMgr.Net.Core.Web</AssemblyName>
<Description>SaBusinessMgrCoreWebPortal</Description>
<PackageId>SaBusinessMgrCoreWebPortal</PackageId>
<Authors>Sarand</Authors>
<Product>BusinessMgr</Product>
<Version>3.0.0</Version>
</PropertyGroup>
<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
<CopyRefAssembliesToPublishDirectory>false</CopyRefAssembliesToPublishDirectory>
<RootNamespace>Sarand.BusinessMgr.Net.Core.Web</RootNamespace>
<AssemblyName>Sarand.BusinessMgr.Net.Core.Web</AssemblyName>
<Description>SaBusinessMgrCoreWebPortal</Description>
<PackageId>SaBusinessMgrCoreWebPortal</PackageId>
<Authors>Sarand</Authors>
<Product>BusinessMgr</Product>
<Version>3.0.0</Version>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="jQuery.UI.Combined" Version="1.12.1" />
<PackageReference Include="Microsoft.AspNetCore.Mvc.Razor.RuntimeCompilation" Version="3.1.16" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="jQuery.UI.Combined" Version="1.13.2" />
<PackageReference Include="Microsoft.AspNetCore.Mvc.Razor.RuntimeCompilation" Version="6.0.31" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\..\Common-VS\Workspace\SacsCoreWeb\SacsCoreWeb.csproj" />
<ProjectReference Include="..\..\..\Common-VS\Workspace\SacsStandardBase\SacsStandardBase.csproj" />
<ProjectReference Include="..\..\..\Common-VS\Workspace\SacsStandardDbas\SacsStandardDbas.csproj" />
<ProjectReference Include="..\..\..\Common-VS\Workspace\SacsStandardProc\SacsStandardProc.csproj" />
<ProjectReference Include="..\SaBusMgrCoreProcs\SaBusMgrCoreProcs.csproj" />
<ProjectReference Include="..\SaBusMgrCoreWeb\SaBusMgrCoreWeb.csproj" />
<ProjectReference Include="..\SaBusMgrStandardBase\SaBusMgrStandardBase.csproj" />
<ProjectReference Include="..\SaBusMgrStandardDbas\SaBusMgrStandardDbas.csproj" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\..\Common-VS\Workspace\SacsCoreWeb\SacsCoreWeb.csproj" />
<ProjectReference Include="..\..\..\Common-VS\Workspace\SacsStandardBase\SacsStandardBase.csproj" />
<ProjectReference Include="..\..\..\Common-VS\Workspace\SacsStandardDbas\SacsStandardDbas.csproj" />
<ProjectReference Include="..\..\..\Common-VS\Workspace\SacsStandardProc\SacsStandardProc.csproj" />
<ProjectReference Include="..\SaBusMgrCoreProcs\SaBusMgrCoreProcs.csproj" />
<ProjectReference Include="..\SaBusMgrCoreWeb\SaBusMgrCoreWeb.csproj" />
<ProjectReference Include="..\SaBusMgrStandardBase\SaBusMgrStandardBase.csproj" />
<ProjectReference Include="..\SaBusMgrStandardDbas\SaBusMgrStandardDbas.csproj" />
</ItemGroup>
<ItemGroup>
<Content Update="appsettings.Machine.Sarand107.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
<ExcludeFromSingleFile>true</ExcludeFromSingleFile>
<CopyToPublishDirectory>PreserveNewest</CopyToPublishDirectory>
</Content>
</ItemGroup>
<ItemGroup>
<Content Update="appsettings.Machine.Sarand110.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
<ExcludeFromSingleFile>true</ExcludeFromSingleFile>
<CopyToPublishDirectory>PreserveNewest</CopyToPublishDirectory>
</Content>
</ItemGroup>
<ItemGroup>
<Folder Include="Views\Association\" />
<Folder Include="Views\Transactions\" />
</ItemGroup>
<ItemGroup>
<Folder Include="Views\Transactions\" />
</ItemGroup>
</Project>
@@ -0,0 +1,135 @@
@model Model_Association
@inject SbmLocaliser Localiser
@{
/* Associations - Index */
ViewData["Title"] = "Associations";
}
<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">
<div class="col-md-5 row">
<div class="col">
@* Filter Panel *@
@using (@Html.BeginForm(FormMethod.Post, new { @id = "frmAssociationFilter", @class = "sbm-form", @onSubmit = "return false;" }))
{
<div class="sbm-panel-filter">
@* Header - Page caption, paging controls, actions. *@
<div class="sbm-header">
<div class="row">
<div class="col"><div class="sbm-caption">@Localiser["association.caption.associations"]</div></div>
<div class="fixed row">
<div class="col">
<partial name="~/Views/Common/ResultsPaging.cshtml" />
</div>
<div class="fixed row">
@* 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>
</div>
<div class="sbm-content">
@* Association filter - name etc. *@
<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.AssociationSearch, new { @id = "", @class = "FilterAssociation", @maxlength = "32" })
</div>
</div>
@* Association Category *@
<div class="row pb4">
<div class="col-sm-5 sbm-label text-left text-sm-right">@Localiser["label.type"]</div>
<div class="col-sm-7">
@Html.DropDownListFor(model => model.Filter.AssociationType, Model.GetAssociationTypeCodesForSelect(), Localiser["label.any"], new { @id = "", @class = "FilterAssociationType sbm-select" })
</div>
</div>
</div>
@* Filter element templates *@
<div class="sbm-panel-item-template">
@* Burger dropdown item template - ul is ignored *@
<ul class="sbm-dropdown-menu AssociationFilter">
<li class="sbm-dropdown-item sbm-action"><div class="sbm-label">XXX</div></li>
</ul>
</div>
</div>
}
</div>
@* Separate filter from results when alondside each other. *@
<div class="fixed"><div class="d-none d-md-block" style="width:8px;"></div></div>
</div>
<div class="col-md-7">
@* Results Panel *@
<div class="sbm-panel-results">
<div class="sbm-panel-messages"></div>
<div class="sbm-panel-item-list"></div>
@* Element templates *@
<div class="sbm-panel-item-template">
@* Association item template *@
<div class="sbm-panel-item Association">
<div class="sbm-header row">
<div class="col">
@* Association caption *@
<div class="sbm-caption"><div class="sbm-action sbm-item-update"><span class="AssociationCode"></span> - <span class="AssociationTargetName"></span></div></div>
</div>
<div class="fixed sbm-actions">
@* 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="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 class="sbm-content">
<div class="row">
<div class="col-sm-4"><div class="AssociationType"></div></div>
<div class="col-sm-4"><div class="AssociationAssociationCount sbm-action-text">@Localiser["label.associations"]:&nbsp;<span class="AssociationCount"></span></div></div>
<div class="col-sm-4 text-left text-sm-right"><div class="StatusDesc"></div></div>
</div>
<div class="row">
<div class=""><div class="AssociationAddress"></div></div>
</div>
<div class="row">
<div class=""><div class="AssociationNotes sbm-note"></div></div>
</div>
</div>
</div>
@* Burger dropdown item template - ul is ignored *@
<ul class="sbm-dropdown-menu Association">
<li class="sbm-dropdown-item sbm-action"><div class="sbm-label">XXX</div></li>
</ul>
</div>
</div>
</div>
</div>
</div>
<partial name="Panel-AssociationDialog.cshtml" />
@@ -0,0 +1,81 @@
@model Model_Association
@inject SbmLocaliser Localiser
@{
/* Associations - Association dialog panel */
}
<script type="text/javascript">
sbm.dlg.title.createAssociation = '@Localiser.GetJavascriptText("association.caption.association.create")';
sbm.dlg.title.updateAssociation = '@Localiser.GetJavascriptText("association.caption.association.update")';
</script>
@using (@Html.BeginForm(FormMethod.Post, new { @id = "frmAssociationId", @class = "sbm-form" }))
{
@Html.HiddenFor(model => model.AssociationId, new { @class = "AssociationId" })
}
<div id="dlgAssociation" class="sbm-dialog" title="">
@using (@Html.BeginForm(FormMethod.Post, new { @id = "frmAssociation", @class = "sbm-form", @onSubmit = "return false;" }))
{
@Html.HiddenFor(model => model.AssociationId, new { @id = "", @class = "AssociationId" })
@Html.HiddenFor(model => model.AssociationExists, new { @id = "", @class = "AssociationExists" });
@* Association Type *@
<div class="sbm-form-control AssociationType">
<div class="sbm-label">
<label for="cbAssociationType"><span>@Localiser["label.type"]</span><span class="required">&nbsp;*</span></label>
</div>
<div class="sbm-control">
@Html.DropDownListFor(model => model.AssociationType, Model.GetAssociationTypeCodesForSelect(), Localiser["label.select"], new { @id = "cbAssociationType", @class = "AssociationType sbm-select" })
</div>
<div class="sbm-message"></div>
</div>
@* Association Code *@
<div class="sbm-form-control AssociationCode">
<div class="sbm-label">
<label for="ifAssociationCode"><span>@Localiser["label.code"]</span><span class="required">&nbsp;*</span></label>
</div>
<div class="sbm-control">
@Html.TextBoxFor(model => model.AssociationCode, new { @id = "ifAssociationCode", @class = "AssociationCode sbm-input", @maxlength = @Model.GetControlMaxLength(SbmDbDfn.ColumnIdx.Association_Code) })
</div>
<div class="sbm-message"></div>
</div>
@* Association Description *@
<div class="sbm-form-control AssociationDesc">
<div class="sbm-label">
<label for="ifAssociationDescDetail"><span>@Localiser["label.description"]</span><span class="required">&nbsp;*</span></label>
</div>
<div class="sbm-control">
@Html.TextBoxFor(model => model.AssociationDescDetail, new { @id = "ifAssociationDescDetail", @class = "AssociationDescDetail sbm-input", @maxlength = @Model.GetControlMaxLength(SbmDbDfn.ColumnIdx.Association_DescDetail) })
</div>
<div class="sbm-message"></div>
</div>
@* Association status *@
<div class="sbm-form-control AssociationStatus">
<div class="sbm-label">
<label for="cbAssociationStatus"><span>@Localiser["label.status"]</span><span class="required">&nbsp;*</span></label>
</div>
<div class="sbm-control">
<select id="cbAssociationStatus" class="sbm-select AssociationStatus" name="AssociationStatusCode"></select>
</div>
<div class="sbm-message"></div>
</div>
@* Association Notes *@
<div class="sbm-form-control AssociationNotes">
<div class="sbm-label">
<label for="ifAssociationNotes"><span>@Localiser["label.notes"]</span></label>
</div>
<div class="sbm-control">
@Html.TextAreaFor(model => model.AssociationNotes, new { @id = "ifAssociationNotes", @class = "AssociationNotes sbm-textarea", @rows = "3", @maxlength = @Model.GetControlMaxLength(SbmDbDfn.ColumnIdx.Association_Notes) })
</div>
<div class="sbm-message"></div>
</div>
}
</div>
@@ -85,8 +85,9 @@
<div class="sbm-panel-messages"></div>
<div class="sbm-panel-item-list"></div>
@* element templates *@
@* Element templates *@
<div class="sbm-panel-item-template">
@* Commodity item template *@
<div class="sbm-panel-item Commodity">
<div class="sbm-header row">
@@ -118,10 +119,12 @@
</div>
</div>
</div>
@* Burger dropdown item template - ul is ignored *@
<ul class="sbm-dropdown-menu Commodity">
<li class="sbm-dropdown-item sbm-action"><div class="sbm-label">XXX</div></li>
</ul>
</div>
</div>
@@ -14,6 +14,17 @@
@using (@Html.BeginForm(FormMethod.Post, new { @id = "frmRequestPassword", @class = "sbm-form" }))
{
<div class="spacer4"></div>
<div class="sbm-form-control DbIdentifier">
<div class="sbm-label">
<label for="ifEmailAddress"><span>@Localiser["label.context"]</span><span class="required">&nbsp;*</span></label>
</div>
<div class="sbm-control">
@Html.TextBoxFor(model => model.DbIdentifier, new { @id = "ifDbIdentifier", @class = "sbm-input", @maxlength = @Sbm.Config.Database.Identifier.MaxLength })
</div>
<div class="sbm-message"></div>
</div>
<div class="spacer4"></div>
<div class="sbm-form-control EmailAddress">
<div class="sbm-label">
@@ -3,7 +3,6 @@
@inject SbmLocaliser Localiser
@{
ViewData["Title"] = "Signin";
ulong emailAddressLen = SaSecurityDbDfn.GetColTextLen(SaSecurityDbDfn.ColumnIdx.SaSecItem_EmailAddress);
}
<div class="">
@@ -12,23 +11,34 @@
<div class="sbm-header"><div class="sbm-caption">@Localiser["label.signin"]</div></div>
<div class="sbm-content">
<div class="sbm-content" style="max-height:none;">
@using (@Html.BeginForm(FormMethod.Post, new { @id = "frmSignIn", @class = "sbm-form" }))
{
<div class="spacer4"></div>
<div class="sbm-form-control DbIdentifier">
<div class="sbm-label">
<label for="ifEmailAddress"><span>@Localiser["label.context"]</span><span class="required">&nbsp;*</span></label>
</div>
<div class="sbm-control">
@Html.TextBoxFor(model => model.DbIdentifier, new { @id = "ifDbIdentifier", @class = "sbm-input", @maxlength = @Sbm.Config.Database.Identifier.MaxLength })
</div>
<div class="sbm-message"></div>
</div>
<div class="spacer2"></div>
<div class="sbm-form-control EmailAddress">
<div class="sbm-label">
<label for="ifEmailAddress"><span>@Localiser["label.email"]</span><span class="required">&nbsp;*</span></label>
</div>
<div class="sbm-control">
@Html.TextBoxFor(model => model.EmailAddress, new { @id = "ifEmailAddress", @class = "sbm-input lower", @maxlength = @emailAddressLen })
@Html.TextBoxFor(model => model.EmailAddress, new { @id = "ifEmailAddress", @class = "sbm-input lower", @maxlength = @Model.GetControlMaxLength(SaSecurityDbDfn.ColumnIdx.SaSecItem_EmailAddress) })
</div>
<div class="sbm-message"></div>
</div>
<div class="spacer4"></div>
<div class="spacer2"></div>
<div class="sbm-form-control Password">
<div class="row">
<div class="col-6">
@@ -43,7 +53,7 @@
<div class="sbm-message"></div>
</div>
<div class="spacer4"></div>
<div class="spacer2"></div>
<div class="sbm-form-control">
<div class="row">
<div class="col-6">
@@ -122,6 +122,7 @@
<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">
@@ -151,7 +152,7 @@
@* Footer *@
<footer id="sbm-page-footer" class="">
<div class="container">
&copy; 2021 - Sarand BusinessMgr Portal
@(Model.CopyrightNotice) - Sarand BusinessMgr Portal
</div>
</footer>
@@ -1,4 +1,4 @@
@using Sarand.BusinessMgr.Net.Standard.Base.App
@using Sarand.BusinessMgr.Net.Core.Web.App
@using Sarand.BusinessMgr.Net.Core.Web.App
@using Sarand.BusinessMgr.Net.Standard.Base.App
@using Sarand.BusinessMgr.Net.Core.Web.Portal.Models
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers
@@ -1,30 +0,0 @@
{
"AppConfigs": {
"SaBusMgr-Dev": {
"Environments": [ "Development" ],
"Instance": "Dev",
"ControlDatabase": {
"Location": "MsSql:Sarand107\\SQL2016.SaBusinessMgr_Control",
"User": "SaBusMgr-Control",
"Password": "sbm-c0Ntrul-dev-2021"
},
"BusMgrDatabase": {
"Location": "MsSql:Sarand107\\SQL2016.SaBusinessMgr_Cadix",
"User": "SaBusMgr-Cadix",
"Password": "sbm-cul-dev-2021"
},
"Email": {
"Context": "SBM-Dev",
"ContactUs": {
"AddressesTo": [ "dev@SaBusinessMgr.com" ]
}
},
"Documents": {
"RootFolder": "D:\\Sarand\\Projects\\Users\\AHoward\\VS2019\\BusinessMgr\\Docs"
},
"Logging": {
"RootFolder": "D:\\Sarand\\Projects\\Users\\AHoward\\VS2019\\BusinessMgr\\Logs\\WebPortal"
}
}
}
}
@@ -0,0 +1,45 @@
{
"AppConfigs": {
"SaBusMgr-Dev": {
"Environments": [ "Development" ],
"Instance": "Dev",
"ControlDatabase": {
"Location": "MsSql:Roman\\SQL2016.SaBusinessMgr_Control",
"User": "sapphire",
"Password": "sarand"
},
"BusMgrDatabases": [
{
"Identifiers": [ "Sarand" ],
"Location": "MsSql:Roman\\SQL2016.SaBusinessMgr_Sarand_Dev",
"User": "sapphire",
"Password": "sarand"
},
{
"Identifiers": [ "Cadix" ],
"Location": "MsSql:Roman\\SQL2016.SaBusinessMgr_Cadix",
"User": "sapphire",
"Password": "sarand"
},
{
"Identifiers": [ "BPE" ],
"Location": "MsSql:Roman\\SQL2016.SaBusinessMgr_BPE",
"User": "sapphire",
"Password": "sarand"
}
],
"Email": {
"Context": "SBM-Dev",
"ContactUs": {
"AddressesTo": [ "dev@SaBusinessMgr.com" ]
}
},
"Documents": {
"RootFolder": "D:\\Sarand\\Projects\\Users\\AHoward\\VS2022\\BusinessMgr\\Docs"
},
"Logging": {
"RootFolder": "D:\\Sarand\\Projects\\Users\\AHoward\\VS2022\\BusinessMgr\\Logs\\WebPortal"
}
}
}
}
@@ -0,0 +1,189 @@
/*
* Association page scripts.
*/
(function (win, doc, undef)
{
"use strict";
sbm.itemIdPrefix = 'Itm_';
let $pnl = sbm.$pnl;
let $frm = sbm.$frm;
let $dlg = sbm.$dlg;
//--------------------------------------------------------------
sbm.refreshResults = function (delay)
{
if (!sbm.awaitingRefreshResponse)
{
$('.sbm-burger', $pnl.filter).hide();
$('.sbm-panel-item-list', $pnl.results).fadeOut(200).empty();
$('.sbm-panel-messages', $pnl.results).hide().empty().append($('<div class="sbm-message">' + sbm.msg.dataQuerying + '</div>')).fadeIn(200);
}
sbm.ajax.refreshResults = sbm.ajax.refreshResults || { url: sbm.url.base + 'Association/Ajax/List', formId: 'frmAssociationFilter', callbackFn: sbm.doRefreshResults };
sbm.doAjaxRequest(sbm.ajax.refreshResults, delay || 50);
sbm.awaitingRefreshResponse = true;
}
sbm.doRefreshResults = function (response)
{
//sbm.debug('doRefreshResults', response);
delete sbm.awaitingRefreshResponse;
sbm.refreshControlValues(response.controlValues);
var $msgs = $('.sbm-panel-messages', $pnl.results).hide().empty();
var $items = $('.sbm-panel-item-list', $pnl.results).hide().empty();
sbm.refreshPaging(response, '#frmAssociationFilter', sbm.refreshResults);
if (!response.success) return;
// Reference data - Statuses.
if (response.defaultStatusCode) sbm.data.defaultStatusCode = response.defaultStatusCode;
if (response.statuses)
sbm.populateSelectCodeDesc('#cbAssociationStatus', response.statuses);
// Menu.
var $bgr = $('.sbm-burger', $pnl.filter).hide();
var $mnu = $('ul.sbm-dropdown-menu', $bgr).empty();
$mnu.append(sbm.getFilterMenuOption(sbm.lbl.refresh, function () { sbm.refreshResults(); }));
$mnu.append(sbm.getFilterMenuOption(sbm.lbl.create, function () { sbm.createItem(); }));
$bgr.toggle(!!$('.sbm-dropdown-item', $mnu).length);
// Items.
var items = response.associations;
if (!items.length)
return $msgs.append($('<div class="sbm-message">' + sbm.msg.dataNoneFound + '</div>')).fadeIn(500);
for (let itemIdx = 0; itemIdx < items.length; itemIdx++)
$items.append(sbm.populateItem(sbm.getItemTemplate(), items[itemIdx]));
$items.fadeIn(50);
}
sbm.populateItem = function ($item, item)
{
if (!item) return;
$item.prop('id', sbm.itemIdPrefix + item.id);
$('.AssociationCode', $item).text(item.code);
$('.AssociationTargetName', $item).text(item.targetName);
$('.AssociationType', $item).text(item.typeDesc);
//$('.AssociationTransactionCount', $item).on('click', function () { sbm.onClickTransactionCount(item.id); }).toggle(item.associationCount).find('.TransactionCount').text(item.associationCount);
$('.AssociationAddress', $item).text(item.addressSummary);
$('.AssociationNotes', $item).text(item.notes);
$('.StatusDesc', $item).text(item.statusDesc).addClass('sbm-status-' + item.statusCode.toLowerCase());
$('.sbm-caption .sbm-action').toggleClass('sbm-action', !item.deleted);
let $mnu = $('ul.sbm-dropdown-menu', $item);
if (!item.deleted)
{
$mnu.append(sbm.getItemMenuOption(sbm.lbl.edit, function () { sbm.updateItem(item.id) }));
$('.sbm-header .sbm-action.sbm-item-update', $item).off('click').on('click', function () { sbm.updateItem(item.id) });
$item.off('dblclick').on('dblclick', function () { sbm.updateItem(item.id) });
}
$('.sbm-burger', $item).toggle(!!$('.sbm-dropdown-item', $mnu).length);
$('.sbm-panel-messages', $pnl.results).hide().empty();
return $item;
}
//--------------------------------------------------------------
sbm.getFilterDropdownTemplate = function ()
{
return $('.sbm-panel-item-template .sbm-dropdown-menu.AssociationFilter li.sbm-dropdown-item', $pnl.filter).clone();
}
sbm.getItemTemplate = function ()
{
return $('.sbm-panel-item-template .sbm-panel-item.Association', $pnl.results).clone();
}
sbm.getItemDropdownTemplate = function ()
{
return $('.sbm-panel-item-template .sbm-dropdown-menu.Association li.sbm-dropdown-item', $pnl.results).clone();
}
sbm.getFilterMenuOption = function (lbl, fn)
{
return sbm.getMenuOption(sbm.getFilterDropdownTemplate(), lbl, fn);
}
sbm.getItemMenuOption = function (lbl, fn)
{
return sbm.getMenuOption(sbm.getItemDropdownTemplate(), lbl, fn);
}
sbm.getMenuOption = function ($opt, lbl, fn)
{
return $opt.find('div.sbm-label').text(lbl).end().on('click', fn);
}
//--------------------------------------------------------------
sbm.createItem = function ()
{
let frm = $frm.association;
$('.AssociationId,.AssociationExists,input[type="text"],textarea,select', frm).val('');
$('.AssociationStatus', frm).val(sbm.data.defaultStatusCode);
sbm.clearControlErrors();
sbm.$dlg.association.dialog('option', 'title', sbm.dlg.title.createAssociation);
sbm.openDialog(sbm.$dlg.association, 600, 600);
}
//--------------------------------------------------------------
sbm.updateItem = function (id)
{
$('.AssociationId', $frm.associationId).val(id);
sbm.ajax.updateItem = sbm.ajax.updateItem || { url: sbm.url.base + 'Association/Ajax/GetItem', formId: 'frmAssociationId', callbackFn: sbm.onUpdateItem };
sbm.doAjaxRequest(sbm.ajax.updateItem, 50);
}
sbm.onUpdateItem = function (response)
{
sbm.debug('onUpdateItem', response);
let frm = $frm.association;
$('.AssociationId,input[type="text"],textarea,select', frm).val('');
if (!response.success) return;
let item = response.association;
$('.AssociationId', frm).val(item.id);
$('.AssociationExists', frm).val(1);
$('.AssociationType', frm).val(item.typeCode);
$('.AssociationCode', frm).val(item.code);
$('.AssociationDesc', frm).val(item.desc);
$('.AssociationNotes', frm).val(item.notes);
$('.AssociationStatus', frm).val(item.statusCode);
sbm.clearControlErrors();
sbm.$dlg.association.dialog('option', 'title', sbm.dlg.title.updateAssociation);
sbm.openDialog(sbm.$dlg.association, 600, 600);
}
//--------------------------------------------------------------
sbm.storeItem = function ()
{
sbm.ajax.storeItem = sbm.ajax.storeItem || { url: sbm.url.base + 'Association/Ajax/StoreItem', formId: 'frmAssociation', callbackFn: sbm.onStoreItem };
sbm.doAjaxRequest(sbm.ajax.storeItem, 50);
}
sbm.onStoreItem = function (response)
{
//sbm.debug('onStoreItem', response);
sbm.refreshControlValues(response.controlValues);
if (!response.success) return;
sbm.$dlg.association.dialog('close');
var item = response.association;
var $item = $('#' + sbm.itemIdPrefix + item.id);
if (!$item.length)
{
$item = sbm.getItemTemplate();
$('.sbm-panel-item-list', $pnl.results).prepend($item).show();
}
$item.replaceWith(sbm.populateItem(sbm.getItemTemplate(), item));
}
//--------------------------------------------------------------
$(function ()
{
$pnl.associations = $('.sbm-panel.Associations');
$pnl.filter = $('.sbm-panel-filter', $pnl.associations);
$pnl.results = $('.sbm-panel-results', $pnl.associations);
$('.sbm-input, .sbm-textarea').on('input', function () { sbm.clearControlError(this); });
$('.sbm-select').on('change', function () { sbm.clearControlError(this); });
$frm.filter = $('#frmAssociationFilter');
$('.FilterAssociation', $frm.filter).on('input', function () { sbm.refreshResults(250); });
$('.FilterAssociationType,.FilterAssociationStatusCategory', $frm.filter).on('change', function () { sbm.refreshResults(50); });
$frm.associationId = $('#frmAssociationId');
$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'); };
sbm.$dlg.association = $('#dlgAssociation').dialog(opt);
setTimeout(sbm.refreshResults, 250);
});
}(window, window.document));
/* The End */
@@ -23,18 +23,6 @@
sbm.doAjaxRequest(sbm.ajax.refreshResults, delay || 50);
sbm.awaitingRefreshResponse = true;
}
sbm.getFilterDropdownTemplate = function ()
{
return $('.sbm-panel-item-template .sbm-dropdown-menu.CommodityFilter li.sbm-dropdown-item', $pnl.filter).clone();
}
sbm.getItemTemplate = function ()
{
return $('.sbm-panel-item-template .sbm-panel-item.Commodity', $pnl.results).clone();
}
sbm.getItemDropdownTemplate = function ()
{
return $('.sbm-panel-item-template .sbm-dropdown-menu.Commodity li.sbm-dropdown-item', $pnl.results).clone();
}
sbm.doRefreshResults = function (response)
{
//sbm.debug('doRefreshResults', response);
@@ -87,6 +75,18 @@
}
//--------------------------------------------------------------
sbm.getFilterDropdownTemplate = function ()
{
return $('.sbm-panel-item-template .sbm-dropdown-menu.CommodityFilter li.sbm-dropdown-item', $pnl.filter).clone();
}
sbm.getItemTemplate = function ()
{
return $('.sbm-panel-item-template .sbm-panel-item.Commodity', $pnl.results).clone();
}
sbm.getItemDropdownTemplate = function ()
{
return $('.sbm-panel-item-template .sbm-dropdown-menu.Commodity li.sbm-dropdown-item', $pnl.results).clone();
}
sbm.getFilterMenuOption = function (lbl, fn)
{
return sbm.getMenuOption(sbm.getFilterDropdownTemplate(), lbl, fn);