Consolidate User Context

This commit is contained in:
2021-08-04 21:32:35 +01:00
parent b8cfd87860
commit afd38cea03
126 changed files with 2240 additions and 634 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="commodity.caption.commodities" xml:space="preserve">
<value>Commodities</value>
</data>
<data name="commodity.caption.commodity" xml:space="preserve">
<value>Commodity</value>
</data>
<data name="commodity.caption.commodity.create" xml:space="preserve">
<value>Create Commodity</value>
</data>
<data name="commodity.caption.commodity.update" xml:space="preserve">
<value>UpdateCommodity</value>
</data>
<data name="contact.caption.contact" xml:space="preserve">
<value>Contact</value>
</data>
@@ -180,6 +192,9 @@
<data name="label.create" xml:space="preserve">
<value>Create</value>
</data>
<data name="label.description" xml:space="preserve">
<value>Description</value>
</data>
<data name="label.email" xml:space="preserve">
<value>Email</value>
</data>
@@ -49,7 +49,7 @@ namespace Sarand.BusinessMgr.Net.Core.Web.Portal.App
try
{
properties = new PropertySet();
SbmGlobals.System = (System_Web = new SbmSystem_Web(properties));
SbmGlobals.System = (SbmGlobals_Web.System = (System_Web = new SbmSystem_Web(properties)));
Configuration = configuration;
}
catch (Exception exc)
@@ -28,8 +28,8 @@ namespace Sarand.BusinessMgr.Net.Core.Web.Portal.App
/// <para>SessionContext and UserAccount must be supplied.</para>
/// <para>Organisation is optional.</para>
/// </summary>
public SbmSessionData_Portal(SbmSessionContext sessionContext, SaSecurityItem securityItemUser, SbmAffiliate affiliateUser) :
base(sessionContext, securityItemUser, affiliateUser)
public SbmSessionData_Portal(SbmSessionContext sessionContext, SaSecurityItem securityItemUser, SbmAffiliate affiliateUser, List<SysIdString> supervisedOrganisations, SbmAssociation associationOrganisation, SbmAffiliate affiliateOrganisation) :
base(sessionContext, securityItemUser, affiliateUser, supervisedOrganisations, associationOrganisation, affiliateOrganisation)
{
}
@@ -37,30 +37,34 @@ namespace Sarand.BusinessMgr.Net.Core.Web.Portal.App
// Contact details.
private class UserData_Online : UserData
{
public UserData_Online(SbmSessionContext sessionContext, SaSecurityItem securityItemUser, SbmAffiliate affiliateUser) :
base(sessionContext, securityItemUser, affiliateUser)
public UserData_Online(SbmSessionContext sessionContext, SaSecurityItem securityItemUser, SbmAffiliate affiliateUser, List<SysIdString> supervisedOrganisations) :
base(sessionContext, securityItemUser, affiliateUser, supervisedOrganisations)
{ }
public UserData_Online(UserData userData) :
base(userData)
{ }
public new bool Refresh(SaSecurityItem securityItemUser, SbmAffiliate affiliateUser)
public new bool Refresh(SaSecurityItem securityItemUser, SbmAffiliate affiliateUser, List<SysIdString> supervisedOrganisations)
{
return base.Refresh(securityItemUser, affiliateUser);
return base.Refresh(securityItemUser, affiliateUser, supervisedOrganisations);
}
}
protected override UserData CreateUserData(SbmSessionContext sessionContext, SaSecurityItem securityItemUser, SbmAffiliate affiliateUser) { return new UserData_Online(sessionContext, securityItemUser, affiliateUser); }
protected override UserData CreateUserData(SbmSessionContext sessionContext, SaSecurityItem securityItemUser, SbmAffiliate affiliateUser, List<SysIdString> supervisedOrganisations)
{
return new UserData_Online(sessionContext, securityItemUser, affiliateUser, supervisedOrganisations);
}
/// <summary>User - JSON deserialisation route.</summary>
public new UserData User { get { return base.User; } set { SetUser(new UserData_Online(value)); } }
//public new UserData User { get { return base.User; } set { SetUser(new UserData_Online(value)); } }
/// <summary>
/// Update the user's contect details against the session.
/// Update the user's contact details against the session.
/// <para>- User identity must match that already against the session.</para>
/// </summary>
public bool RefreshUser(SaSecurityItem securityItemUser, SbmAffiliate affiliateUser)
public bool RefreshUser(SaSecurityItem securityItemUser, SbmAffiliate affiliateUser, List<SysIdString> supervisedOrganisations, SbmAssociation associationOrganisation, SbmAffiliate affiliateOrganisation)
{
UserData_Online user = User as UserData_Online;
if (user != null)
return user.Refresh(securityItemUser, affiliateUser);
return user.Refresh(securityItemUser, affiliateUser, supervisedOrganisations);
organisationData = new OrganisationData(associationOrganisation, affiliateOrganisation);
return false;
}
@@ -0,0 +1,285 @@
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 Commodity Controller - Ajax actions.
//=======================================================================
partial class CommodityController
{
//-------------------------------------------------------------------
// Commodity data.
private class CommodityData_List : AjaxResponse_Data.ItemData
{
public String TypeCode { get; set; }
public String TypeDesc { get; set; }
public String Code { get; set; } = "";
public String Desc { get; set; } = "";
// Address.
public String AddressSummary { get; set; }
}
private class CommodityData_Full : CommodityData_List
{
// Commodity properties.
}
private void PopulateCommodityResponseData(SbmCommodity commodity, CommodityData_List data)
{
data.Id = SysId.ToString(commodity.Id);
data.TypeCode = Internals.CommodityTypeCodes.GetCodeForType(commodity.CommodityType);
data.TypeDesc = Internals.CommodityTypeCodes.GetDescForType(commodity.CommodityType);
data.Code = commodity.Code;
data.Desc = commodity.DescBrief;
// Common properties.
data.Notes = commodity.Notes;
data.StatusCode = commodity.StatusCode;
data.StatusDesc = commodity.StatusDesc;
}
private void PopulateCommodityResponseData(SbmCommodity commodity, CommodityData_Full data)
{
PopulateCommodityResponseData(commodity, data as CommodityData_List);
// Commodity properties.
}
//-------------------------------------------------------------------
// Commodity list.
//-------------------------------------------------------------------
private class AjaxResponse_CommodityList : AjaxResponse_Data
{
// Construction.
public AjaxResponse_CommodityList(SbmModel_Portal model) : base(model) { }
// Commodity list.
public List<CommodityData_List> Commodities { get; } = new List<CommodityData_List>();
// Reference data.
public List<CodeDesc> Statuses { get; } = new List<CodeDesc>();
public String DefaultStatusCode { get; set; }
}
// Ajax request.
[HttpPost]
[Route("Commodity/Ajax/List")]
[Route("Commodity/Ajax/GetList")]
public IActionResult Ajax_CommodityList(Model_Commodity model)
{
String fn = GetType().Name + ".Ajax_CommodityList";
AjaxResponse_CommodityList responseData = new AjaxResponse_CommodityList(model);
String messageTitle = Localiser["commodity.caption.commodities"];
// Validate session and referrer.
if (!ValidUserSession) return GetAjaxResult_InvalidSession(responseData);
if (!ValidReferer) return Forbid();
RefreshModel(model);
// Reference data - statuses.
foreach (SaStatus status in SbmCommodity.ValidStatuses.FindAll(item => item != SaStatus.Deleted))
responseData.Statuses.Add(SacsSystem.GetStatusCode(status), Localiser.GetStatusCaption(status));
responseData.DefaultStatusCode = SacsSystem.GetStatusCode(SbmCommodity.DefaultStatus);
// Item filter.
SbmCommodity.Params prmCommodity = new SbmCommodity.Params();
prmCommodity.AffiliateIdOrganisation = SessionData.Organisation.AffiliateId;
prmCommodity.Search = model.Filter.CommoditySearch;
prmCommodity.CommodityType = model.GetFilterCommodityType();
prmCommodity.StatusCat = SbmCommodity.StatusCat.NotDeleted;
prmCommodity.SortColumns.Add(CommodityMgr.GetCommodityColumnDfn(SbmCommodity.Column.Code));
prmCommodity.SortColumns.Add(CommodityMgr.GetCommodityColumnDfn(SbmCommodity.Column.DescBrief));
model.Paging.ItemTotal = (ulong)CommodityMgr.GetCommodityCount(prmCommodity);
model.PopulatePagingTo(prmCommodity.ResultsPaging);
// Item columns.
ColumnDfn colId = CommodityMgr.GetCommodityColumnDfn(SbmCommodity.Column.Id);
ColumnDfn colCommodityType = CommodityMgr.GetCommodityColumnDfn(SbmCommodity.Column.Type_Desc);
ColumnDfn colCode = CommodityMgr.GetCommodityColumnDfn(SbmCommodity.Column.Code);
ColumnDfn colDescBrief = CommodityMgr.GetCommodityColumnDfn(SbmCommodity.Column.DescBrief);
ColumnDfn colNotes = CommodityMgr.GetCommodityColumnDfn(SbmCommodity.Column.Notes);
ColumnDfn colStatus = CommodityMgr.GetCommodityColumnDfn(SbmCommodity.Column.Status);
ColumnDfn colCreatedOn = CommodityMgr.GetCommodityColumnDfn(SbmCommodity.Column.CreateOn_Date);
ColumnDfn colUpdatedOn = CommodityMgr.GetCommodityColumnDfn(SbmCommodity.Column.UpdateOn_Date);
List<ColumnDfn> columns = new List<ColumnDfn>()
{
colId, colCommodityType, colCode, colDescBrief,
colNotes, colStatus, colCreatedOn, colUpdatedOn
};
// Query items.
using (DbResultSet resultSet = CommodityMgr.ExecuteCommodityQuery(columns, prmCommodity))
{
while (resultSet != null && resultSet.HasRows && resultSet.NextRecord())
{
NamedValue.Map values = resultSet.GetRecordValues();
Sbm.CommodityType commodityType = (Sbm.CommodityType)values.GetLong(colCommodityType.DataName);
CommodityData_List commodityData = new CommodityData_List()
{
Id = values.GetString(colId.DataName),
TypeCode = Internals.CommodityTypeCodes.GetCodeForType(commodityType),
TypeDesc = Internals.CommodityTypeCodes.GetDescForType(commodityType),
Code = values.GetString(colCode.DataName),
Desc= values.GetString(colDescBrief.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.Commodities.Add(commodityData);
}
}
// Refresh paging info.
if (model.Paging.IsPaged)
model.Paging.ItemCount = (ushort)responseData.Commodities.Count;
SessionData.Commodities.Filter.PageNo = model.Paging.PageNo;
SessionData.Commodities.Filter.PageLength = model.Paging.PageLength;
responseData.SetPaging(model);
// Complete response.
responseData.Success = true;
return Json(responseData);
}
//-------------------------------------------------------------------
// Get Commodity ajax request.
//-------------------------------------------------------------------
private class AjaxResponse_Commodity : AjaxResponse_Data
{
// Construction.
public AjaxResponse_Commodity(SbmModel_Portal model) : base(model) { }
// Commodity details.
public CommodityData_Full commodity { get; } = new CommodityData_Full();
}
//-------------------------------------------------------------------
[HttpPost]
[Route("Commodity/Ajax/Item")]
[Route("Commodity/Ajax/GetItem")]
public IActionResult Ajax_GetItem(Model_Commodity model)
{
String fn = GetType().Name + ".Ajax_GetItem";
String messageTitle = Localiser["commodity.caption.commodity"];
AjaxResponse_Commodity responseData = new AjaxResponse_Commodity(model);
// Validate session.
if (!ValidUserSession)
return GetAjaxResult_InvalidSession(responseData);
// Identify item.
SysId commodityId = model.CommodityId.ToSysId();
if (!SysId.IsValid(commodityId))
return Json(responseData.SetMessage(messageTitle, Localiser["msg.error.item.id.not.specified"]));
// Retrieve item.
SbmCommodity commodity = CommodityMgr.GetCommodityById(commodityId);
if (commodity == null)
{
SaLogger.Warn(fn + " Failed to retrieve commodity for Id: '" + model.CommodityId + "'");
return Json(responseData.SetMessage(messageTitle, Localiser["msg.error.item.id.not.found"]));
}
// Construct response.
PopulateCommodityResponseData(commodity, responseData.commodity);
// Complete response.
responseData.Success = true;
return Json(responseData);
}
//-------------------------------------------------------------------
// Store Commodity ajax request.
//-------------------------------------------------------------------
[HttpPost]
[Route("Commodity/Ajax/Store")]
[Route("Commodity/Ajax/StoreItem")]
public IActionResult Ajax_StoreItem(Model_Commodity model)
{
String fn = GetType().Name + ".Ajax_StoreItem";
String messageTitle = Localiser["commodity.caption.commodity"];
AjaxResponse_Commodity responseData = new AjaxResponse_Commodity(model);
// Validate session.
if (!ValidUserSession)
return GetAjaxResult_InvalidSession(responseData);
// Validate item properties.
model.ValidateCommodityData(responseData);
if (responseData.HasControlErrors())
return Json(responseData);
// Retrieve existing item.
SysId commodityId = model.CommodityId.ToSysId();
SbmCommodity commodity = CommodityMgr.GetCommodityById(commodityId);
if (commodity == null && SysId.IsValid(commodityId))
{
SaLogger.Warn(fn + " Failed to retrieve commodity for Id: '" + model.CommodityId + "'");
return Json(responseData.SetMessage(messageTitle, Localiser["msg.error.item.id.not.found"]));
}
// Create new item if required.
if (commodity == null)
{
commodity = CommodityMgr.CreateCommodity(SessionData.Organisation.AffiliateId, model.GetCommodityType(), model.CommodityCode);
if (model.CommodityCode != null)
commodity.Code = model.CommodityCode;
}
// Populate item properties.
if (model.CommodityCode != null)
commodity.Code = model.CommodityCode;
// Common properties.
if (model.CommodityNotes != null)
commodity.Notes = model.CommodityNotes;
if (model.CommodityStatus != null)
commodity.Status = model.GetCommodityStatus();
// Store item.
if (!SysId.IsValid(commodity.Id))
{
if (!CommodityMgr.InsertCommodity(commodity, DbUpdateInfo))
{
SaLogger.Warn(fn + " Failed to insert commodity: " + CommodityMgr.ErrorStateMessage);
return Json(responseData.SetMessage(messageTitle, Localiser["msg.error.item.insert.failed"] + "\n\n" + CommodityMgr.ErrorStateMessage));
}
}
else
{
if (!CommodityMgr.UpdateCommodity(commodity, DbUpdateInfo))
{
SaLogger.Warn(fn + " Failed to update commodity [Id:" + SysId.ToString(commodity.Id) + "]: " + CommodityMgr.ErrorStateMessage);
return Json(responseData.SetMessage(messageTitle, Localiser["msg.error.item.update.failed"] + "\n\n" + CommodityMgr.ErrorStateMessage));
}
}
// Construct response.
PopulateCommodityResponseData(commodity, responseData.commodity);
// 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 CommodityController : SbmController_Portal
{
//-------------------------------------------------------------------
// Data members.
//-------------------------------------------------------------------
// Construction.
public CommodityController(SbmLocaliser localiser, ILogger<CommodityController> logger) :
base(localiser, logger)
{
}
//-------------------------------------------------------------------
[HttpGet]
public IActionResult Index()
{
String fn = GetType().Name + ".Index";
if (!ValidUserSession)
return RedirectToAction("", "");
// Commodity page.
Model_Commodity model = new Model_Commodity(Localiser);
model.Paging.PageNo = SessionData.Commodities.Filter.PageNo;
model.Paging.PageLength = SessionData.Commodities.Filter.PageLength;
return View(model);
}
//-------------------------------------------------------------------
}
//=======================================================================
}
@@ -167,17 +167,16 @@ namespace Sarand.BusinessMgr.Net.Core.Web.Portal.Controllers
ColumnDfn colAffiliateType = AccountMgr.GetAffiliateColumnDfn(SbmAffiliate.Column.Type);
ColumnDfn colPhoneMain = AccountMgr.GetAffiliateColumnDfn(SbmAffiliate.Column.PhoneMain);
ColumnDfn colPhoneAlt = AccountMgr.GetAffiliateColumnDfn(SbmAffiliate.Column.PhoneAlt);
ColumnDfn colAddress = AccountMgr.GetAffiliateColumnDfn(SbmAffiliate.Column.Address_Summary);
ColumnDfn colNotes = AccountMgr.GetAffiliateColumnDfn(SbmAffiliate.Column.Notes);
ColumnDfn colStatus = AccountMgr.GetAffiliateColumnDfn(SbmAffiliate.Column.Status);
ColumnDfn colCreatedOn = AccountMgr.GetAffiliateColumnDfn(SbmAffiliate.Column.CreateOn_Date);
ColumnDfn colUpdatedOn = AccountMgr.GetAffiliateColumnDfn(SbmAffiliate.Column.UpdateOn_Date);
List<ColumnDfn> columns = new List<ColumnDfn>()
{
colId,
colName,
colAffiliateType,
colPhoneMain,
colPhoneAlt,
colId, colName, colAffiliateType,
colPhoneMain, colPhoneAlt,
colAddress,
colNotes, colStatus, colCreatedOn, colUpdatedOn
};
@@ -194,18 +193,27 @@ namespace Sarand.BusinessMgr.Net.Core.Web.Portal.Controllers
DisplayName = values.GetString(colName.DataName),
TypeCode = Internals.AffiliateTypeCodes.GetCodeForType(affiliateType),
TypeDesc = Internals.AffiliateTypeCodes.GetDescForType(affiliateType),
AddressSummary = AccountMgr.GetAffiliateDataValue(colAddress, values).AsString(),
Notes = values.GetString(colNotes.DataName),
StatusCode = values.GetString(colStatus.DataName),
StatusDesc = SacsSystem.GetStatusDescForCode(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.Affiliates.Add(affiliateData);
}
}
// Refresh paging info.
if (model.Paging.IsPaged)
model.Paging.ItemCount = (ushort)responseData.Affiliates.Count;
SessionData.Contacts.Filter.PageNo = model.Paging.PageNo;
SessionData.Contacts.Filter.PageLength = model.Paging.PageLength;
responseData.SetPaging(model);
// Complete response.
responseData.SetPaging(model);
responseData.Success = true;
return Json(responseData);
}
@@ -47,13 +47,6 @@ namespace Sarand.BusinessMgr.Net.Core.Web.Portal.Controllers
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 });
}
//-------------------------------------------------------------------
}
//=======================================================================
@@ -60,16 +60,19 @@ namespace Sarand.BusinessMgr.Net.Core.Web.Portal.Controllers
using (SbmAccountMgr accountMgrForUser = App.System.CreateAccountMgr(userContext))
{
using (SaSecurityItem securityItemUser = securityConsoleForUser.GetUserByEmail(keepLoggedInParams.EmailAddress))
using (SbmAffiliate affiliateUser = accountMgrForUser.GetAffiliateById(keepLoggedInParams.AffiliateId))
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)
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, true, ref errorMessage))
if (SignUserIn(userContext, securityItemUser, affiliateUser, supervisedOrganisations, associationOrganisation, affiliateOrganisation, true, ref errorMessage))
{
SaLogger.Info(logPrefix + " 'Keep Logged In' cookie found." + logSuffix);
break;
@@ -4,6 +4,7 @@ using Sarand.BusinessMgr.Net.Core.Web.Controllers;
using Sarand.BusinessMgr.Net.Core.Web.Portal.App;
using Sarand.BusinessMgr.Net.Standard.Base.App;
using Sarand.BusinessMgr.Net.Standard.Base.Bus;
using Sarand.BusinessMgr.Net.Standard.Base.Core;
using Sarand.BusinessMgr.Net.Standard.Base.Mgr;
using Sarand.Net.Standard.Base.App;
using Sarand.Net.Standard.Base.Bus;
@@ -51,7 +52,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(SbmUserContext userContext, SaSecurityItem securityItemUser, SbmAffiliate affiliateUser, bool keepLoggedIn, ref String errorMessage)
protected bool SignUserIn(ISbmUserContext userContext, SaSecurityItem securityItemUser, SbmAffiliate affiliateUser, List<SysIdString> supervisedOrganisations, SbmAssociation associationOrganisation, SbmAffiliate affiliateOrganisation, bool keepLoggedIn, ref String errorMessage)
{
String fn = GetType().Name + ".SignUserIn";
@@ -75,6 +76,26 @@ namespace Sarand.BusinessMgr.Net.Core.Web.Portal.Controllers
return false;
}
// Organisation.
if (associationOrganisation == null)
{
SaLogger.Error(fn + " Unable to sign account in - null organisation association.");
errorMessage = Localiser["security.msg.signin.invalid"];
return false;
}
if (associationOrganisation.AssociationType != Sbm.AssociationType.Internal)
{
SaLogger.Error(fn + " Unable to sign account in - organisation association is not type 'Internal'.");
errorMessage = Localiser["security.msg.signin.invalid"];
return false;
}
if (affiliateOrganisation == null)
{
SaLogger.Error(fn + " Unable to sign account in - null organisation affiliate.");
errorMessage = Localiser["security.msg.signin.invalid"];
return false;
}
// 'Keep logged in' can be switched off by the application configuration.
keepLoggedIn = App.AppConfig.Security.SignIn.KeepLoggedIn.Allowed && keepLoggedIn;
@@ -104,7 +125,7 @@ namespace Sarand.BusinessMgr.Net.Core.Web.Portal.Controllers
}
// Create session.
OpenUserSession(userContext.SessionContext, securityItemUser, affiliateUser);
OpenUserSession(userContext.SessionContext, securityItemUser, affiliateUser, supervisedOrganisations, associationOrganisation, affiliateOrganisation);
// Send the 'Keep Logged In' cookie if requested.
if (affiliateUser.KeepLoggedIn_Portal)
@@ -115,7 +136,7 @@ namespace Sarand.BusinessMgr.Net.Core.Web.Portal.Controllers
//-------------------------------------------------------------------
/// <summary>Populate the user's profile contact details to the session.</summary>
protected bool RefreshSessionProfileContact(SaSecurityItem securityItemUser, SbmAffiliate affiliateUser)
protected bool RefreshSessionProfileContact(SaSecurityItem securityItemUser, SbmAffiliate affiliateUser, List<SysIdString> supervisedOrganisations, SbmAssociation associationOrganisation, SbmAffiliate affiliateOrganisation)
{
String fn = GetType().Name + ".RefreshSessionProfileContact";
SbmSessionData_Portal sessionData = SessionData_Portal;
@@ -123,15 +144,16 @@ namespace Sarand.BusinessMgr.Net.Core.Web.Portal.Controllers
if (!SysId.IsValid(affiliateIdUser)) return false;
if (!SysId.IsValid(affiliateUser?.Id ?? null)) return false;
if (!affiliateIdUser.Equals(affiliateUser.Id)) return false;
if (!SysId.IsValid(affiliateOrganisation?.Id ?? null)) return false;
sessionData.RefreshUser(securityItemUser, affiliateUser);
sessionData.RefreshUser(securityItemUser, affiliateUser, supervisedOrganisations, associationOrganisation, affiliateOrganisation);
return true;
}
//-------------------------------------------------------------------
/// <summary>Start a new user session on sign-in.</summary>
protected bool OpenUserSession(SbmSessionContext sessionContext, SaSecurityItem securityItemUser, SbmAffiliate affiliateUser)
protected bool OpenUserSession(SbmSessionContext sessionContext, SaSecurityItem securityItemUser, SbmAffiliate affiliateUser, List<SysIdString> supervisedOrganisations, SbmAssociation associationOrganisation, SbmAffiliate affiliateOrganisation)
{
// Validate user properties.
if (!SysId.IsValid(affiliateUser.Id)) return false;
@@ -141,7 +163,7 @@ namespace Sarand.BusinessMgr.Net.Core.Web.Portal.Controllers
CloseUserSession();
// Open session.
return SetSessionData(new SbmSessionData_Portal(sessionContext, securityItemUser, affiliateUser));
return SetSessionData(new SbmSessionData_Portal(sessionContext, securityItemUser, affiliateUser, supervisedOrganisations, associationOrganisation, affiliateOrganisation));
}
protected void CloseUserSession()
{
@@ -185,13 +207,15 @@ namespace Sarand.BusinessMgr.Net.Core.Web.Portal.Controllers
// Keep-logged-in cookie value.
private static readonly String ParamName_KeepLoggedIn_EmailAddress = "ema";
private static readonly String ParamName_KeepLoggedIn_SecurityItemId = "sii";
private static readonly String ParamName_KeepLoggedIn_AffiliateId = "afi";
private static readonly String ParamName_KeepLoggedIn_AffiliateIdUser = "afiu";
private static readonly String ParamName_KeepLoggedIn_AssociationIdOrganisation = "asio";
private String GenerateKeepLoggedInCookieValue()
{
NameValueCollection keepLoggedInParams = new NameValueCollection();
keepLoggedInParams.Add(ParamName_KeepLoggedIn_EmailAddress, SessionData_Portal.User.EmailAddress);
keepLoggedInParams.Add(ParamName_KeepLoggedIn_SecurityItemId, SysId.ToString(SessionData_Portal.User.SecurityItemId));
keepLoggedInParams.Add(ParamName_KeepLoggedIn_AffiliateId, SysId.ToString(SessionData_Portal.User.AffiliateId));
keepLoggedInParams.Add(ParamName_KeepLoggedIn_AffiliateIdUser, SysId.ToString(SessionData_Portal.User.AffiliateId));
keepLoggedInParams.Add(ParamName_KeepLoggedIn_AssociationIdOrganisation, SysId.ToString(SessionData_Portal.Organisation.AssociationId));
return EncryptParameters(keepLoggedInParams, Base64IV_KeepLoggedIn, Base64Key_KeepLoggedIn);
}
@@ -200,7 +224,8 @@ namespace Sarand.BusinessMgr.Net.Core.Web.Portal.Controllers
{
public String EmailAddress { get; set; }
public SysId SecurityItemId { get; set; }
public SysId AffiliateId { get; set; }
public SysId AffiliateIdUser { get; set; }
public SysId AssociationIdOrganisation { get; set; }
/// <summary>Retrieve the list of valid parameters in printable format [Name: Value].</summary>
public List<String> GetValidParams()
{
@@ -209,8 +234,10 @@ namespace Sarand.BusinessMgr.Net.Core.Web.Portal.Controllers
values.Add("EmailAddress: " + EmailAddress);
if (SysId.IsValid(SecurityItemId))
values.Add("SecurityItemId: " + SysId.ToString(SecurityItemId));
if (SysId.IsValid(AffiliateId))
values.Add("UserAccountId: " + SysId.ToString(AffiliateId));
if (SysId.IsValid(AffiliateIdUser))
values.Add("AffiliateIdUser: " + SysId.ToString(AffiliateIdUser));
if (SysId.IsValid(AssociationIdOrganisation))
values.Add("AssociationIdOrganisation: " + SysId.ToString(AssociationIdOrganisation));
return values;
}
/// <summary>Verifiy that the email and account Ids are populated and the data hasn't expired.</summary>
@@ -219,7 +246,8 @@ namespace Sarand.BusinessMgr.Net.Core.Web.Portal.Controllers
return
!StringUtil.IsEmpty(EmailAddress) &&
SysId.IsValid(SecurityItemId) &&
SysId.IsValid(AffiliateId);
SysId.IsValid(AffiliateIdUser) &&
SysId.IsValid(AssociationIdOrganisation);
}
}
public KeepLoggedInParams ExtractKeepLoggedInParams(String encryptedParams)
@@ -230,9 +258,10 @@ namespace Sarand.BusinessMgr.Net.Core.Web.Portal.Controllers
NameValueCollection linkParams = DecryptParameters(encryptedParams, Base64IV_KeepLoggedIn, Base64Key_KeepLoggedIn);
keepLoggedInParams.EmailAddress = linkParams.Get(ParamName_KeepLoggedIn_EmailAddress);
keepLoggedInParams.SecurityItemId = linkParams.Get(ParamName_KeepLoggedIn_SecurityItemId).ToSysId();
keepLoggedInParams.AffiliateId = linkParams.Get(ParamName_KeepLoggedIn_AffiliateId).ToSysId();
keepLoggedInParams.AffiliateIdUser = linkParams.Get(ParamName_KeepLoggedIn_AffiliateIdUser).ToSysId();
keepLoggedInParams.AssociationIdOrganisation = linkParams.Get(ParamName_KeepLoggedIn_AssociationIdOrganisation).ToSysId();
}
if (DebugMode) SaLogger.Info(GetType().Name + ".ExtractKeepLoggedInParams UserAccountId: " + SysId.ToString(keepLoggedInParams.AffiliateId));
if (DebugMode) SaLogger.Info(GetType().Name + ".ExtractKeepLoggedInParams UserAccountId: " + SysId.ToString(keepLoggedInParams.AffiliateIdUser));
return keepLoggedInParams;
}
@@ -5,6 +5,7 @@ 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.Core;
using Sarand.BusinessMgr.Net.Standard.Base.Helpers;
using Sarand.BusinessMgr.Net.Standard.Base.Mgr;
using Sarand.Net.Standard.Base.App;
@@ -44,7 +45,7 @@ namespace Sarand.BusinessMgr.Net.Core.Web.Portal.Controllers
{
String fn = GetType().Name + ".Ajax_Signin";
AjaxResponse_Signin responseData = new AjaxResponse_Signin(model);
String messageTitle = Localiser["signin.caption"];
String messageTitle = Localiser["label.signin"];
// Validate referrer.
if (!ValidReferer) return Forbid();
@@ -61,20 +62,52 @@ namespace Sarand.BusinessMgr.Net.Core.Web.Portal.Controllers
SbmSessionContext sessionContext = SbmSessionContextHelper_Web.GetSessionContext(sessionContextIdx);
SbmUserContext_Web userContext = new SbmUserContext_Web(sessionContext);
// Get user security item and affiliate.
using (SaSecurityItem securityItemUser = GetSecurityItemForSignin(userContext, model, responseData))
using (DisposableCollection<IDisposable> disposableItems = new DisposableCollection<IDisposable>())
{
if (responseData.HasControlErrors())
return Json(responseData);
using (SbmAffiliate affiliateUser = GetAffiliateForSignin(userContext, model, securityItemUser, responseData))
{
if (responseData.HasControlErrors())
return Json(responseData);
// Account manager.
SaSecurityConsole securityConsoleForUser = App.CreateSecurityConsole(userContext);
disposableItems.Add(securityConsoleForUser);
SbmAccountMgr accountMgrForUser = SbmGlobals_Web.System.CreateAccountMgr(userContext);
disposableItems.Add(accountMgrForUser);
String errorMessage = "";
if (!SignUserIn(userContext, securityItemUser, affiliateUser, model.KeepMeSignedIn, ref errorMessage))
return Json(responseData.SetControlError(nameof(model.EmailAddress), errorMessage));
// 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"]));
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"]));
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"]));
}
// 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"]));
disposableItems.Add(associationOrganisation);
// User's organisation affiliate.
SbmAffiliate affiliateOrganisation = accountMgrForUser.GetAffiliateById(associationOrganisation?.Id ?? null);
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"]));
}
String errorMessage = "";
if (!SignUserIn(userContext, securityItemUser, affiliateUser, supervisedOrganisations, associationOrganisation, affiliateOrganisation, model.KeepMeSignedIn, ref errorMessage))
return Json(responseData.SetControlError(nameof(model.EmailAddress), errorMessage));
}
}
@@ -84,6 +117,81 @@ namespace Sarand.BusinessMgr.Net.Core.Web.Portal.Controllers
return Json(responseData);
}
//-------------------------------------------------------------------
private SaSecurityItem GetSecurityItemForSignin(SaSecurityConsole securityConsoleForUser, Model_Security model)
{
String fn = GetType().Name + ".GetSecurityItemForSignin";
// Retrieve security item by email address.
SaSecurityItem securityItemUser = securityConsoleForUser.GetUserByEmail(model.EmailAddress);
if (securityItemUser == null)
{
SaLogger.Warn(fn + " Failed to retrieve Security Item for email: " + model.EmailAddress, true);
return null;
}
// Validate password.
if (!SbmPasswordHelper.VerifyHashedPassword(securityItemUser, model.Password) && !(DebugMode && model.EmailAddress.EqualsCI("ahoward@sarand.com")))
{
// Record invalid login attempt.
securityItemUser.InvalidLoginCount++;
securityItemUser.LastInvalidLoginAt = DateTime.Now;
if (!securityConsoleForUser.UpdateSecurityItem(securityItemUser, DbUpdateInfo))
SaLogger.Warn(fn + " Failed to store invalid sign-in attempt against User: " + model.EmailAddress + ". " + AccountMgr.ErrorStateMessage, true);
SaLogger.Warn(fn + " Attempt to sign-in to User " + model.EmailAddress + " with invalid password. Attempt: " + securityItemUser.InvalidLoginCount, true);
securityItemUser.Dispose(); securityItemUser = null;
return null;
}
return securityItemUser;
}
//-------------------------------------------------------------------
private SbmAffiliate GetAffiliateForSignin(SbmAccountMgr accountMgrForUser, Model_Security model, SaSecurityItem securityItemUser)
{
String fn = GetType().Name + ".GetAffiliateForSignin";
// Identify the contact asociated with the security item.
SbmAffiliate.Params prmAffiliate = new SbmAffiliate.Params();
prmAffiliate.UserId = securityItemUser.Id;
prmAffiliate.StatusCat = SbmAffiliate.StatusCat.Any;
SbmAffiliate affiliate = accountMgrForUser.GetAffiliate(prmAffiliate);
if (affiliate == null)
{
SaLogger.Warn(fn + " Failed to retrieve Affiliate for User Id: " + SysId.ToString(securityItemUser.Id) + ". Email: " + model.EmailAddress, true);
return null;
}
// Validate status.
if (affiliate.Status != SaStatus.Current)
{
SaLogger.Warn(fn + " Invalid Affiliate status for User Id: " + SysId.ToString(securityItemUser.Id) + ". Email: " + model.EmailAddress + ". Found: " + affiliate.Status.ToString() + ". Expected: Current.", true);
affiliate.Dispose(); affiliate = null;
return null;
}
return affiliate;
}
//-------------------------------------------------------------------
private SbmAssociation GetOrganisationAssociationForSignIn(SbmAccountMgr accountMgrForUser, Model_Security model, SbmAffiliate affiliateUser, List<SysIdString> supervisedOrganisations)
{
String fn = GetType().Name + ".GetOrganisationAssociationForSignIn";
foreach (SysIdString supervisedOrganisation in supervisedOrganisations)
{
SbmAssociation associationOrganisation = accountMgrForUser.GetAssociationByOwnerTypeTarget(supervisedOrganisation.Id, Sbm.AssociationType.Internal, supervisedOrganisation.Id);
if (associationOrganisation == null)
{
SaLogger.Warn(fn + " Failed to retrieve internal association for affiliate Id: " + SysId.ToString(supervisedOrganisation.Id));
continue;
}
return associationOrganisation;
}
SaLogger.Warn(fn + " Failed to retrieve any internal associations for supervised organisations. Email: " + model.EmailAddress);
return null;
}
//-------------------------------------------------------------------
// Keep Logged In.
//-------------------------------------------------------------------
@@ -117,59 +225,6 @@ namespace Sarand.BusinessMgr.Net.Core.Web.Portal.Controllers
return Json(responseData);
}
//-------------------------------------------------------------------
private SaSecurityItem GetSecurityItemForSignin(SbmUserContext_Web userContext, Model_Security model, AjaxResponse_Data responseData)
{
String fn = GetType().Name + ".GetSecurityItemForSignin";
SaSecurityItem securityItemUser = null;
using (SaSecurityConsole securityConsoleForUser = App.CreateSecurityConsole(userContext))
{
// Retrieve security item by email address.
using (securityItemUser = securityConsoleForUser.GetUserByEmail(model.EmailAddress))
{
if (securityItemUser != null)
{
// Validate password.
if (!SbmPasswordHelper.VerifyHashedPassword(securityItemUser, model.Password) && !(DebugMode && model.EmailAddress.EqualsCI("ahoward@sarand.com")))
{
// Record invalid login attempt.
securityItemUser.InvalidLoginCount++;
securityItemUser.LastInvalidLoginAt = DateTime.Now;
if (!securityConsoleForUser.UpdateSecurityItem(securityItemUser, DbUpdateInfo))
SaLogger.Warn(fn + " Failed to store invalid sign-in attempt against User: " + model.EmailAddress + ". " + AccountMgr.ErrorStateMessage, true);
SaLogger.Warn(fn + " Attempt to sign-in to User " + model.EmailAddress + " with invalid password. Attempt: " + securityItemUser.InvalidLoginCount, true);
responseData.SetControlError(nameof(model.EmailAddress), model.Localiser["security.msg.signin.invalid"]);
securityItemUser = null;
}
}
}
}
return securityItemUser;
}
//-------------------------------------------------------------------
private SbmAffiliate GetAffiliateForSignin(SbmUserContext_Web userContext, Model_Security model, SaSecurityItem securityItemUser, AjaxResponse_Data responseData)
{
String fn = GetType().Name + ".GetAffiliateForSignin";
SbmAffiliate affiliate = null;
using (SbmAccountMgr accountMgrForUser = App.System.CreateAccountMgr(userContext))
{
// Identify the contact asociated with the security item.
SbmAffiliate.Params prmAffiliate = new SbmAffiliate.Params();
prmAffiliate.UserId = securityItemUser.Id;
prmAffiliate.StatusCat = SbmAffiliate.StatusCat.CurrentOnly;
using (affiliate = accountMgrForUser.GetAffiliate(prmAffiliate))
{
if (affiliate != null)
{
}
if (affiliate == null && !responseData.HasControlErrors())
responseData.SetControlError(nameof(model.EmailAddress), model.Localiser["security.msg.signin.invalid"]);
}
}
return affiliate;
}
//-------------------------------------------------------------------
// Sign-out request.
//-------------------------------------------------------------------
@@ -185,7 +240,7 @@ namespace Sarand.BusinessMgr.Net.Core.Web.Portal.Controllers
{
String fn = GetType().Name + ".Ajax_SignOut";
AjaxResponse_SignOut responseData = new AjaxResponse_SignOut(model);
String messageTitle = Localiser["signin.caption.signout"];
String messageTitle = Localiser["label.signout"];
// Validate session and referrer.
if (!ValidReferer)
@@ -454,7 +509,7 @@ namespace Sarand.BusinessMgr.Net.Core.Web.Portal.Controllers
responseData.SuccessMsg = Localiser["security.msg.request.password.email.sent"];
return Json(responseData);
}
private void PopulateRequestPasswordEmailBody(SbmEmailConsole emailConsole, SbmUserContext userContext, SaEmailData emailData, SaSecurityItem securityItemUser, SbmAffiliate affiliateUser)
private void PopulateRequestPasswordEmailBody(SbmEmailConsole emailConsole, ISbmUserContext userContext, SaEmailData emailData, SaSecurityItem securityItemUser, SbmAffiliate affiliateUser)
{
// Will need to handle culture (language/country) as and when introduced.
emailData.MessageBody = SbmResourceHelper.GetTextResource(SbmResourceHelper.TextResource.Email_RequestPassword_Html);
@@ -0,0 +1,115 @@
using Sarand.BusinessMgr.Net.Core.Web.App;
using Sarand.BusinessMgr.Net.Core.Web.Controllers;
using Sarand.BusinessMgr.Net.Standard.Base.App;
using Sarand.BusinessMgr.Net.Standard.Base.Bus;
using Sarand.BusinessMgr.Net.Standard.Base.Helpers;
using Sarand.Net.Standard.Base.App;
using Sarand.Net.Standard.Base.Core;
using Sarand.Net.Standard.Base.Helpers;
using Sarand.Net.Standard.Base.Utils;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace Sarand.BusinessMgr.Net.Core.Web.Portal.Models
{
//=======================================================================
public class Model_Commodity : SbmModel_Portal
{
//-------------------------------------------------------------------
// Construction.
public Model_Commodity(SbmLocaliser localiser) :
base(localiser)
{
AdditionalFiles_JS.Add("js/sbm-commodity.js");
}
//-------------------------------------------------------------------
// Filter properties.
public class FilterData
{
public String CommoditySearch { get; set; }
public String CommodityType { get; set; }
}
public FilterData Filter { get; } = new FilterData();
public Sbm.CommodityType GetFilterCommodityType()
{
return (Sbm.CommodityType)Internals.CommodityTypeCodes.GetTypeForCode(Filter.CommodityType);
}
//-------------------------------------------------------------------
// Commodity properties.
public String CommodityId { get; set; }
public String CommodityType{ get; set; }
public Sbm.CommodityType GetCommodityType()
{
return (Sbm.CommodityType)Internals.CommodityTypeCodes.GetTypeForCode(CommodityType);
}
public String CommodityCode { get; set; }
public String CommodityDesc { get; set; }
// Common properties.
public String CommodityNotes { get; set; }
public String CommodityStatus { get; set; }
public SaStatus GetCommodityStatus()
{
return SacsSystem.GetStatusForCode(CommodityStatus);
}
//-------------------------------------------------------------------
// Validation.
//-------------------------------------------------------------------
// Commodity validation.
public bool ValidateCommodityData(SbmController_Base.AjaxResponse_Data responseData)
{
// Commodity Type.
if (CommodityType != null)
{
if (StringUtil.IsEmpty(CommodityType))
responseData.SetControlError(nameof(CommodityType), Localiser["msg.validation.mandatory.value"]);
}
// Commodity Code.
if (CommodityCode != null)
{
responseData.SetControlValue(nameof(CommodityCode), CommodityCode);
if (StringUtil.IsEmpty(CommodityCode))
responseData.SetControlError(nameof(CommodityCode), Localiser["msg.validation.mandatory.value"]);
if (StringUtil.GetLength(CommodityCode) > GetControlMaxLength(SbmDbDfn.ColumnIdx.Commodity_Code))
responseData.SetControlError(nameof(CommodityCode), Localiser["msg.validation.maximum.length", GetControlMaxLength(SbmDbDfn.ColumnIdx.Commodity_Code)]);
}
// Commodity Desc.
if (CommodityDesc != null)
{
responseData.SetControlValue(nameof(CommodityDesc), CommodityDesc);
if (StringUtil.IsEmpty(CommodityDesc))
responseData.SetControlError(nameof(CommodityDesc), Localiser["msg.validation.mandatory.value"]);
if (StringUtil.GetLength(CommodityDesc) > GetControlMaxLength(SbmDbDfn.ColumnIdx.Commodity_DescBrief))
responseData.SetControlError(nameof(CommodityDesc), Localiser["msg.validation.maximum.length", GetControlMaxLength(SbmDbDfn.ColumnIdx.Commodity_DescBrief)]);
}
// Commodity Notes.
if (CommodityNotes != null)
{
responseData.SetControlValue(nameof(CommodityNotes), CommodityNotes);
if (!StringUtil.IsEmpty(CommodityNotes) && StringUtil.GetLength(CommodityNotes) > GetControlMaxLength(SbmDbDfn.ColumnIdx.Commodity_Notes))
responseData.SetControlError(nameof(CommodityNotes), Localiser["msg.validation.maximum.length", GetControlMaxLength(SbmDbDfn.ColumnIdx.Commodity_Notes)]);
}
// Commodity status.
if (CommodityStatus != null)
{
if (StringUtil.IsEmpty(CommodityStatus))
responseData.SetControlError(nameof(CommodityStatus), Localiser["msg.validation.mandatory.value"]);
else if (!SbmCommodity.ValidStatuses.Contains(GetCommodityStatus()))
responseData.SetControlError(nameof(CommodityStatus), Localiser["msg.validation.invalid.value"]);
}
return !responseData.HasControlErrors();
}
//-------------------------------------------------------------------
}
//=======================================================================
}
@@ -37,7 +37,6 @@
</ItemGroup>
<ItemGroup>
<Folder Include="Views\Commodity\" />
<Folder Include="Views\Association\" />
<Folder Include="Views\Transactions\" />
</ItemGroup>
@@ -0,0 +1,117 @@
@model Model_Commodity
@inject SbmLocaliser Localiser
@{
/* Commodities - Index */
ViewData["Title"] = "Commodities";
}
<div class="sbm-page-commodities">
<script type="text/javascript">
sbm.lbl.edit = 'Edit';
sbm.lbl.activate = 'Activate';
</script>
<div class="row">
<div class="col-md-5 row">
<div class="col">
@* Filter Panel *@
@using (@Html.BeginForm(FormMethod.Post, new { @id = "frmCommodityFilter", @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["commodity.caption.commodities"]</div></div>
<div class="fixed row">
<div class="col">
<partial name="~/Views/Common/ResultsPaging.cshtml" />
</div>
<div class="fixed row">
<div class="sbm-action sbm-results-refresh"><img src="~/img/action/refresh24.png" alt="@(Localiser["label.refresh"])" title="@(Localiser["label.refresh"])" /></div>
<div class="sbm-action sbm-item-create"><img src="~/img/action/add24.png" alt="@(Localiser["label.create"])" title="@(Localiser["label.create"])" /></div>
</div>
</div>
</div>
</div>
<div class="sbm-content">
@* Commodity 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.CommoditySearch, new { @id = "", @class = "FilterCommodity", @maxlength = "32" })
</div>
</div>
@* Commodity 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.CommodityType, Model.GetCommodityTypeCodesForSelect(), Localiser["label.any"], new { @id = "", @class = "FilterCommodityType sbm-select" })
</div>
</div>
</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">
@* Commodity item template *@
<div class="sbm-panel-item Commodity">
<div class="sbm-header row">
<div class="col">
@* Commodity caption *@
<div class="sbm-caption"><div class="sbm-action sbm-item-update CommodityName"></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="CommodityType"></div></div>
<div class="col-sm-4"><div class="CommodityAssociationCount 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="CommodityAddress"></div></div>
</div>
<div class="row">
<div class=""><div class="CommodityNotes sbm-note"></div></div>
</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>
</div>
</div>
</div>
<partial name="Panel-CommodityDialog.cshtml" />
@@ -0,0 +1,67 @@
@model Model_Commodity
@inject SbmLocaliser Localiser
@{
/* Commodities - Commodity dialog panel */
}
<script type="text/javascript">
sbm.dlg.title.createCommodity = '@Localiser.GetJavascriptText("commodity.caption.commodity.create")';
sbm.dlg.title.updateCommodity = '@Localiser.GetJavascriptText("commodity.caption.commodity.update")';
</script>
@using (@Html.BeginForm(FormMethod.Post, new { @id = "frmCommodityId", @class = "sbm-form" }))
{
@Html.HiddenFor(model => model.CommodityId, new { @class = "CommodityId" })
}
<div id="dlgCommodity" class="sbm-dialog" title="">
@using (@Html.BeginForm(FormMethod.Post, new { @id = "frmCommodity", @class = "sbm-form", @onSubmit = "return false;" }))
{
@Html.HiddenFor(model => model.CommodityId, new { @id = "", @class = "CommodityId" })
@* Commodity Type *@
<div class="sbm-form-control CommodityType">
<div class="sbm-label">
<label for="cbCommodityType"><span><span class="required">*</span> @Localiser["label.type"]</span></label>
</div>
<div class="sbm-control">
@Html.DropDownListFor(model => model.CommodityType, Model.GetCommodityTypeCodesForSelect(), Localiser["label.select"], new { @id = "cbCommodityType", @class = "CommodityType sbm-select" })
</div>
<div class="sbm-message"></div>
</div>
@* Commodity Codeription *@
<div class="sbm-form-control CommodityCode">
<div class="sbm-label">
<label for="ifCommodityCode"><span><span class="required">*</span> @Localiser["label.code"]</span></label>
</div>
<div class="sbm-control">
@Html.TextBoxFor(model => model.CommodityCode, new { @id = "ifCommodityCode", @class = "CommodityCode sbm-input", @maxlength = @Model.GetControlMaxLength(SbmDbDfn.ColumnIdx.Commodity_Code) })
</div>
<div class="sbm-message"></div>
</div>
@* Commodity Description *@
<div class="sbm-form-control CommodityDesc">
<div class="sbm-label">
<label for="ifCommodityDesc"><span><span class="required">*</span> @Localiser["label.description"]</span></label>
</div>
<div class="sbm-control">
@Html.TextBoxFor(model => model.CommodityDesc, new { @id = "ifCommodityDesc", @class = "CommodityDesc sbm-input", @maxlength = @Model.GetControlMaxLength(SbmDbDfn.ColumnIdx.Commodity_DescBrief) })
</div>
<div class="sbm-message"></div>
</div>
@* Commodity Notes *@
<div class="sbm-form-control CommodityNotes">
<div class="sbm-label">
<label for="ifCommodityNotes"><span><span class="required">*</span> @Localiser["label.notes"]</span></label>
</div>
<div class="sbm-control">
@Html.TextAreaFor(model => model.CommodityNotes, new { @id = "ifCommodityNotes", @class = "CommodityNotes sbm-textarea", @maxlength = @Model.GetControlMaxLength(SbmDbDfn.ColumnIdx.Commodity_Notes) })
</div>
<div class="sbm-message"></div>
</div>
}
</div>
@@ -47,7 +47,7 @@
</div>
@* Affiliate Category *@
<div class="row pb4">
<div class="col-sm-5 sbm-label text-left text-sm-right">@Localiser["label.category"]</div>
<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.AffiliateType, Model.GetAffiliateTypeCodesForSelect(), Localiser["label.any"], new { @id = "", @class = "FilterAffiliateType sbm-select" })
</div>
@@ -92,14 +92,15 @@
<div class="sbm-content">
<div class="row">
<div class="col-sm-4"><div class="AffiliateType"></div></div>
<div class="col-sm-4"><div class=""></div></div>
<div class="col-sm-4 text-left text-sm-right"><div class=""></div></div>
</div>
<div class="row">
<div class="col-sm-4"><div class=""></div></div>
<div class="col-sm-4"><div class="AffiliateAssociationCount 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="AffiliateAddress"></div></div>
</div>
<div class="row">
<div class=""><div class="AffiliateNotes sbm-note"></div></div>
</div>
</div>
</div>
@* Burger dropdown item template - ul is ignored *@
@@ -121,8 +121,6 @@
</a>
<div class="dropdown-menu" aria-labelledby="navbarDropdown">
<div class="dropdown-item text-dark sbm-action" onclick="sbm.goToPage('Contact');">@Localiser["label.contacts"]</div>
</div>
<div class="dropdown-menu" aria-labelledby="navbarDropdown">
<div class="dropdown-item text-dark sbm-action" onclick="sbm.goToPage('Commodity');">@Localiser["label.commodities"]</div>
</div>
</li>
@@ -0,0 +1,184 @@
/*
* Commodity page scripts.
*/
(function (win, doc, undef)
{
"use strict";
sbm.itemIdPrefix = 'Itm_';
var $results;
//--------------------------------------------------------------
sbm.refreshResults = function (delay)
{
if (!sbm.awaitingRefreshResponse)
{
var $items = $('.sbm-panel-item-list', $results).fadeOut(200).empty();
var $msgs = $('.sbm-panel-messages', $results).hide().empty().append($('<div class="sbm-message">' + sbm.msg.dataQuerying + '</div>')).fadeIn(200);
}
sbm.ajax.refreshResults = sbm.ajax.refreshResults || { url: sbm.url.base + 'Commodity/Ajax/List', formId: 'frmCommodityFilter', callbackFn: sbm.doRefreshResults };
sbm.doAjaxRequest(sbm.ajax.refreshResults, delay || 50);
sbm.awaitingRefreshResponse = true;
}
sbm.getItemTemplate = function ()
{
return $('.sbm-panel-item-template .sbm-panel-item.Commodity', $results);
}
sbm.getDropdownItemTemplate = function ()
{
return $('.sbm-panel-item-template .sbm-dropdown-menu.Commodity li.sbm-dropdown-item', $results);
}
sbm.doRefreshResults = function (response)
{
sbm.debug('doRefreshResults', response);
delete sbm.awaitingRefreshResponse;
sbm.refreshControlValues(response.controlValues);
var $msgs = $('.sbm-panel-messages', $results).hide().empty();
var $items = $('.sbm-panel-item-list', $results).hide().empty();
var $tmpl = sbm.getItemTemplate();
sbm.refreshPaging(response, '#frmCommodityFilter', sbm.refreshResults);
if (!response.success) return;
// Reference data - Statuses.
if (response.defaultStatusCode) sbm.data.defaultStatusCode = response.defaultStatusCode;
if (response.statuses)
sbm.populateSelectCodeDesc('#cbCommodityStatus', response.statuses);
// Items.
var items = response.commodies;
if (!items.length)
return $msgs.append($('<div class="sbm-message">' + sbm.msg.dataNoneFound + '</div>')).fadeIn(500);
for (let itemIdx = 0; itemIdx < items.length; itemIdx++)
{
let item = items[itemIdx];
let $item = $tmpl.clone();
sbm.populateItem($item, item);
$items.append($item);
}
$items.fadeIn(50);
}
sbm.populateItem = function ($item, item)
{
if (!item) return;
$item.prop('id', sbm.itemIdPrefix + item.id);
$('.CommodityName', $item).text(item.displayName);
$('.CommodityType', $item).text(item.typeDesc);
$('.CommodityAssociationCount', $item).on('click', function () { sbm.onClickAssociationCount(item.id); }).toggle(item.associationCount).find('.AssociationCount').text(item.associationCount);
$('.CommodityAddress', $item).text(item.addressSummary);
$('.CommodityNotes', $item).text(item.notes);
$('.StatusDesc', $item).text(item.statusDesc).addClass('sbm-status-' + item.statusCode.toLowerCase());
$('.sbm-caption .sbm-action').toggleClass('sbm-action', !item.deleted);
if (!item.deleted)
{
let $mnu = $('ul.sbm-dropdown-menu', $item);
$mnu.append(sbm.getDropdownItemTemplate().clone().find('div.sbm-label').text(sbm.lbl.edit).end().on('click', function () { sbm.updateItem(item.id) }));
if (item.awaiting)
$mnu.append(sbm.getDropdownItemTemplate().clone().find('div.sbm-label').text(sbm.lbl.activate).end().on('click', function () { sbm.activateItem(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-burger .sbm-dropdown-menu .sbm-dropdown-item', $item).length);
$('.sbm-panel-messages', $results).hide().empty();
return $item;
}
//--------------------------------------------------------------
sbm.createItem = function ()
{
let $frm = sbm.$frm.commodity;
$('.CommodityId,input[type="text"],textarea,select', $frm).val('');
$('.CommodityStatus', $frm).val(sbm.data.defaultStatusCode);
sbm.clearControlErrors();
sbm.$dlg.commodity.dialog('option', 'title', sbm.dlg.title.createCommodity);
sbm.openDialog(sbm.$dlg.commodity, 600, 600);
}
sbm.doCreateItem = function ()
{
sbm.storeItem();
}
//--------------------------------------------------------------
sbm.updateItem = function (id)
{
$('.CommodityId', sbm.$frm.commodityId).val(id);
sbm.ajax.updateItem = sbm.ajax.updateItem || { url: sbm.url.base + 'Commodity/Ajax/GetItem', formId: 'frmCommodityId', callbackFn: sbm.onUpdateItem };
sbm.doAjaxRequest(sbm.ajax.updateItem, 50);
}
sbm.onUpdateItem = function (response)
{
//sbm.debug('onUpdateItem', response);
let $frm = sbm.$frm.commodity;
$('.CommodityId,input[type="text"],textarea,select', $frm).val('');
if (!response.success) return;
let item = response.commodity;
$('.CommodityId', $frm).val(item.id);
$('.CommodityType', $frm).val(item.typeCode);
$('.CommodityName', $frm).val(item.name);
$('.CommodityNotes', $frm).val(item.notes);
$('.CommodityStatus', $frm).val(item.statusCode);
sbm.clearControlErrors();
sbm.$dlg.commodity.dialog('option', 'title', sbm.dlg.title.updateCommodity);
sbm.openDialog(sbm.$dlg.commodity, 600, 600);
}
sbm.doUpdateItem = function ()
{
sbm.storeItem();
}
//--------------------------------------------------------------
sbm.storeItem = function ()
{
sbm.ajax.storeItem = sbm.ajax.storeItem || { url: sbm.url.base + 'Commodity/Ajax/StoreItem', formId: 'frmCommodity', 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.commodity.dialog('close');
var item = response.commodity;
var $item = $('#' + sbm.itemIdPrefix + item.id);
if (!$item.length)
{
$item = $('.sbm-panel-item-template .sbm-panel-item.Commodity', $results).clone();
$('.sbm-panel-item-list', $results).prepend($item).show();
}
$item.replaceWith(sbm.populateItem(sbm.getItemTemplate(), item));
}
//--------------------------------------------------------------
sbm.onClickAssociationCount = function (id)
{
if (!(id * 1)) return;
sbm.doPleaseWait();
let $frm = $('#frmCommodityId');
$('.CommodityId', $frm).val(id);
$frm.prop('action', sbm.url.base + 'Association').submit();
}
//--------------------------------------------------------------
$(function ()
{
$('.sbm-panel-filter .sbm-action.sbm-item-create').on('click', function () { sbm.createItem(); });
$('.sbm-panel-filter .sbm-action.sbm-results-refresh').on('click', function () { sbm.refreshResults(); });
$('.sbm-input, .sbm-textarea').on('input', function () { sbm.clearControlError(this); });
$('.sbm-select').on('change', function () { sbm.clearControlError(this); });
sbm.$frm.filter = $('#frmCommodityFilter');
$('.FilterCommodity', sbm.$frm.filter).on('input', function () { sbm.refreshResults(250); });
$('.FilterCommodityCategory,.FilterCommodityStatusCategory', sbm.$frm.filter).on('change', function () { sbm.refreshResults(50); });
sbm.$frm.commodityId = $('#frmCommodityId');
sbm.$frm.commodity = $('#frmCommodity');
$results = $('.sbm-panel-results');
var opt = sbm.dlgOpts();
opt.buttons[sbm.dlg.btn.ok] = function () { sbm.doCreateItem(); };
opt.buttons[sbm.dlg.btn.cancel] = function () { $(this).dialog('close'); };
sbm.$dlg.commodity = $('#dlgCommodity').dialog(opt);
setTimeout(sbm.refreshResults, 250);
});
}(window, window.document));
/* The End */
@@ -1,5 +1,5 @@
/*
* Admin - Affiliate page scripts.
* Contact page scripts.
*/
(function (win, doc, undef)
@@ -61,7 +61,9 @@
$item.prop('id', sbm.itemIdPrefix + item.id);
$('.AffiliateName', $item).text(item.displayName);
$('.AffiliateType', $item).text(item.typeDesc);
$('.AffiliateAssociationCount', $item).on('click', function () { sbm.onClickAssociationCount(item.id); }).find('.AssociationCount').text(item.associationCount);
$('.AffiliateAssociationCount', $item).on('click', function () { sbm.onClickAssociationCount(item.id); }).toggle(item.associationCount).find('.AssociationCount').text(item.associationCount);
$('.AffiliateAddress', $item).text(item.addressSummary);
$('.AffiliateNotes', $item).text(item.notes);
$('.StatusDesc', $item).text(item.statusDesc).addClass('sbm-status-' + item.statusCode.toLowerCase());
$('.sbm-caption .sbm-action').toggleClass('sbm-action', !item.deleted);
if (!item.deleted)