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
@@ -154,7 +154,6 @@ namespace Sarand.BusinessMgr.Net.Core.Procs.Consoles
//----------------------------------------------------------------
// Log process definition.
SaLogger.Info("Running process: " + processDfn.ProcessName + "." + (processDfn.Params.Count > 0 ? " Params: " + processDfn.Params.Concat() : ""), true);
SbmProcessConsole_Notifier processNotifier = new SbmProcessConsole_Notifier();
//----------------------------------------------------------------
// Hapag-Lloyd EDI.
@@ -170,7 +169,6 @@ namespace Sarand.BusinessMgr.Net.Core.Procs.Consoles
configInterface.ImportNotifications = true;
configInterface.Automated = true;
SbmInterface_System_HapagLloyd_Import process = new SbmInterface_System_HapagLloyd_Import(configInterface);
process.ProcessNotifier = processNotifier;
process.RunProcess();
if (process.HasProcessError())
SaLogger.Warn(caption + " reported: " + process.GetProcessError(), true);
@@ -219,20 +217,4 @@ namespace Sarand.BusinessMgr.Net.Core.Procs.Consoles
//-------------------------------------------------------------------
}
//=======================================================================
/// <summary>
/// Process Notificer for Process Console.
/// </summary>
class SbmProcessConsole_Notifier : ISacsProcessNotifier
{
public void OnStartProcess()
{
}
public void OnEndProcess()
{
}
public void SetProgressInfo(List<string> progressInfo)
{
}
}
//=======================================================================
}
@@ -78,6 +78,10 @@ namespace Sarand.BusinessMgr.Net.Core.Procs.Interfaces
{
this.config = config;
interfaceObj = InterfaceConsole.GetInterfaceById(config.InterfaceId);
// Events.
base.OnProcessStarting += SbmInterfaceProcess_OnProcessStarting;
base.OnProgressInfoUpdated += SbmInterfaceProcess_OnProgressInfoUpdated;
base.OnProcessCompleted += SbmInterfaceProcess_OnProcessCompleted;
}
protected override void Dispose(bool disposing)
{
@@ -132,16 +136,19 @@ namespace Sarand.BusinessMgr.Net.Core.Procs.Interfaces
//-------------------------------------------------------------------
// P r o c e s s i n g E v e n t s
//-------------------------------------------------------------------
protected override void OnProcessStarting()
private void SbmInterfaceProcess_OnProcessStarting(object sender, ProcessStartingEventArgs e)
{
base.OnProcessStarting();
if (config.InterfaceProcessCreation == InterfaceProcessCreation.OnStartProcessing)
OpenInterfaceProcess();
}
protected override void OnProcessEnding()
private void SbmInterfaceProcess_OnProgressInfoUpdated(object sender, ProgressInfoUpdatedEventArgs e)
{
}
private void SbmInterfaceProcess_OnProcessCompleted(object sender, ProcessCompletedEventArgs e)
{
CloseInterfaceProcess();
base.OnProcessEnding();
}
//-------------------------------------------------------------------
@@ -176,13 +183,13 @@ namespace Sarand.BusinessMgr.Net.Core.Procs.Interfaces
{
// Process has not completed.
interfaceProcess.Status = SaStatus.Cancelled;
if (HasProcessError()) LogInterfaceProcessMessage(GetProcessError(), SaMessageLevel.Error);
if (HasErrorMessage()) LogInterfaceProcessMessage(GetErrorMessage(), SaMessageLevel.Error);
if (IsInterrupted) LogInterfaceProcessMessage("Processing cancelled by user", SaMessageLevel.Warning);
}
if (InterfaceConsole.StoreInterfaceProcess(interfaceProcess, DbUpdateInfo))
{ }
else if (!HasProcessError())
SetProcessError("Failed to store interface process: " + InterfaceConsole.ErrorStateMessage);
else if (!HasErrorMessage())
AddErrorMessage("Failed to store interface process: " + InterfaceConsole.ErrorStateMessage);
// Clean-up.
interfaceProcess.Dispose();
@@ -229,7 +236,7 @@ namespace Sarand.BusinessMgr.Net.Core.Procs.Interfaces
// Store interface data item.
if (!InterfaceConsole.StoreInterfaceDataItem(interfaceDataItem, DbUpdateInfo))
SetProcessError("Failed to save Interface data item" + (!StringUtil.IsEmpty(itemCaption) ? " for " + itemCaption : "") + " - " + InterfaceConsole.ErrorStateMessage);
AddErrorMessage("Failed to save Interface data item" + (!StringUtil.IsEmpty(itemCaption) ? " for " + itemCaption : "") + " - " + InterfaceConsole.ErrorStateMessage);
return OK;
}
@@ -29,7 +29,7 @@ namespace Sarand.BusinessMgr.Net.Core.Procs.Interfaces
// Configuration.
public new class Config : SbmInterfaceProcess.Config
{
public Config (ISbmUserContext userContext) : base(userContext)
public Config(ISbmUserContext userContext) : base(userContext)
{
ItemType = Sbm.ItemType.Commodity;
ProcessType = ProcessType.ExportData;
@@ -117,12 +117,12 @@ namespace Sarand.BusinessMgr.Net.Core.Procs.Interfaces
//-------------------------------------------------------------------
// Construction.
public SbmInterfaceProcess_Commodity_Export (Config config) : base(config)
public SbmInterfaceProcess_Commodity_Export(Config config) : base(config)
{
this.config = config;
interfaceOptions = SbmSystem.GetCodesOfType(UserContext, Sbm.SystemCode.InterfaceOption);
}
protected override void Dispose (bool disposing)
protected override void Dispose(bool disposing)
{
if (disposing)
{
@@ -159,7 +159,7 @@ namespace Sarand.BusinessMgr.Net.Core.Procs.Interfaces
//-------------------------------------------------------------------
// Utilities.
private void CalculateItemCount ()
private void CalculateItemCount()
{
if (Params.CommodityCountTotal >= 0 || !OK) return;
Params.CommodityCountTotal = CommodityMgr.GetCommodityCount(GetParams());
@@ -168,7 +168,7 @@ namespace Sarand.BusinessMgr.Net.Core.Procs.Interfaces
//-------------------------------------------------------------------
// I n t e r f a c e P a r a m e t e r s
//-------------------------------------------------------------------
private bool PopulateInterfaceParams ()
private bool PopulateInterfaceParams()
{
interfaceParams = new InterfaceParams();
@@ -230,7 +230,7 @@ namespace Sarand.BusinessMgr.Net.Core.Procs.Interfaces
int columnPos = StringUtil.ToInt(columnOption.Context);
SbmCommodity.Column eCol = (SbmCommodity.Column)optionValues_Column.GetTypeForCode(columnOption.Value);
if (columnPos <= previousColumnPos)
return SetProcessError("Out of sequence column found in configuration: '" + columnOption.Value + "' for column '" + columnOption.Context + "'");
return AddErrorMessage("Out of sequence column found in configuration: '" + columnOption.Value + "' for column '" + columnOption.Context + "'");
if (columnPos > 0 && columnPos <= kiMaxDataColumn)
{
ColumnDfn col = CommodityMgr.GetCommodityColumnDfn(eCol);
@@ -241,9 +241,9 @@ namespace Sarand.BusinessMgr.Net.Core.Procs.Interfaces
interfaceParams.Columns.Add(columnNull);
interfaceParams.Columns[columnPos - 1] = col;
}
else return SetProcessError("Invalid field specified in configuration: '" + columnOption.Value + "' for column '" + columnOption.Context + "'");
else return AddErrorMessage("Invalid field specified in configuration: '" + columnOption.Value + "' for column '" + columnOption.Context + "'");
}
else return SetProcessError("Invalid column specified in configuration: '" + columnOption.Value + "'");
else return AddErrorMessage("Invalid column specified in configuration: '" + columnOption.Value + "'");
}
return true;
}
@@ -251,7 +251,7 @@ namespace Sarand.BusinessMgr.Net.Core.Procs.Interfaces
//-------------------------------------------------------------------
// P r o c e s s i n g
//-------------------------------------------------------------------
protected override bool Run ()
protected override bool Run()
{
SetProgress("Preparing to export commodities.");
SaLogger.Info("Process: " + ProcessDescription);
@@ -265,17 +265,17 @@ namespace Sarand.BusinessMgr.Net.Core.Procs.Interfaces
ExportCommodityData_Csv();
else if (interfaceParams.ExportMode == Sbm.FileInterface_Mode.MsExcel)
ExportCommodityData_Excel();
else return SetProcessError("Invalid Commodity export mode: " + interfaceParams.ExportMode.ToString());
else return AddErrorMessage("Invalid Commodity export mode: " + interfaceParams.ExportMode.ToString());
// Log statistics.
int commoditiesExported = GetStatistic(ProcessingStatistic.CommoditiesExported);
if (commoditiesExported > 0) LogInterfaceProcessMessage("Commodites exported: " + commoditiesExported.ToString());
return !HasProcessError();
return !HasErrorMessage();
}
//-------------------------------------------------------------------
protected override void ResetProcess ()
protected override void ResetProcess()
{
base.ResetProcess();
@@ -288,42 +288,42 @@ namespace Sarand.BusinessMgr.Net.Core.Procs.Interfaces
}
//-------------------------------------------------------------------
protected override bool ValidateConfig ()
protected override bool ValidateConfig()
{
if (!base.ValidateConfig()) return false;
// Configuration:
if (!SysId.IsValid(config.InterfaceId)) return SetProcessError("Interface Id must be supplied.");
if (!config.ExportCommodities) return SetProcessError("No processing options selected.");
if (!SysId.IsValid(config.InterfaceId)) return AddErrorMessage("Interface Id must be supplied.");
if (!config.ExportCommodities) return AddErrorMessage("No processing options selected.");
// Processing parameters.
if (!PopulateInterfaceParams()) return false;
if (interfaceParams.ExportMode <= 0) return SetProcessError("Invalid export mode specified.");
if (interfaceParams.ExportMode <= 0) return AddErrorMessage("Invalid export mode specified.");
if (interfaceParams.ExportMode == Sbm.FileInterface_Mode.Csv && StringUtil.IsEmpty(interfaceParams.OutputFolder))
return SetProcessError("Output folder must be specified for 'CSV' mode.");
return AddErrorMessage("Output folder must be specified for 'CSV' mode.");
if (interfaceParams.FirstDataRow < 1 || interfaceParams.FirstDataRow > 100)
return SetProcessError("Invalid first data row (" + (interfaceParams.FirstDataRow) + ")");
return AddErrorMessage("Invalid first data row (" + (interfaceParams.FirstDataRow) + ")");
// Validate the output folder.
if (!StringUtil.IsEmpty(interfaceParams.OutputFolder) && !DirectoryUtil.DirectoryExists(interfaceParams.OutputFolder))
return SetProcessError("Invalid export folder specified - " + interfaceParams.OutputFolder);
return AddErrorMessage("Invalid export folder specified - " + interfaceParams.OutputFolder);
if (interfaceParams.Columns.Count < 1)
return SetProcessError("At least one data column must be specified");
return AddErrorMessage("At least one data column must be specified");
return OK;
}
//-------------------------------------------------------------------
/// <summary>Construct the commodity filter parameters.</summary>
public SbmCommodity.Params GetParams ()
public SbmCommodity.Params GetParams()
{
SbmCommodity.Params prmCommodity = config.CommodityParams ?? new SbmCommodity.Params();
prmCommodity.IncludedIds.AddRange(config.ItemIds);
return prmCommodity;
}
private DbResultSet ExecuteQuery ()
private DbResultSet ExecuteQuery()
{
if (!OK) return null;
@@ -333,12 +333,12 @@ namespace Sarand.BusinessMgr.Net.Core.Procs.Interfaces
if (!queryColumns.HasId(SbmCommodity.Column.Id)) queryColumns.Add(CommodityMgr.GetCommodityColumnDfn(SbmCommodity.Column.Id));
if (!queryColumns.HasId(SbmCommodity.Column.Code)) queryColumns.Add(CommodityMgr.GetCommodityColumnDfn(SbmCommodity.Column.Code));
// Query data.
SbmCommodity.Params prmCommodity =GetParams();
SbmCommodity.Params prmCommodity = GetParams();
DbResultSet resultSet = CommodityMgr.ExecuteCommodityQuery(queryColumns, prmCommodity);
if (resultSet == null) SetProcessError("Failed to query data - " + CommodityMgr.ErrorStateMessage);
if (resultSet == null) AddErrorMessage("Failed to query data - " + CommodityMgr.ErrorStateMessage);
return resultSet;
}
private DataValue GetDataValue (ColumnDfn column, NamedValue.Map values)
private DataValue GetDataValue(ColumnDfn column, NamedValue.Map values)
{
DataValue value = new DataValue();
SbmCommodity.Column columnId = (SbmCommodity.Column)column.Id;
@@ -362,7 +362,7 @@ namespace Sarand.BusinessMgr.Net.Core.Procs.Interfaces
//-------------------------------------------------------------------
// E x p o r t T o C S V
//-------------------------------------------------------------------
private bool ExportCommodityData_Csv ()
private bool ExportCommodityData_Csv()
{
String fn = this.GetFn("ExportCommodityData_Csv");
if (!OK) return false;
@@ -374,10 +374,10 @@ namespace Sarand.BusinessMgr.Net.Core.Procs.Interfaces
{
// Progress is refreshed every few rows.
Params.CommodityCountTotal = CommodityMgr.GetCommodityCount(GetParams());
int progressRefreshRows = (int)Math.Max(5,Params.CommodityCountTotal/(Params.CommodityCountTotal>1000?1000:100));
int progressRefreshRows = (int)Math.Max(5, Params.CommodityCountTotal / (Params.CommodityCountTotal > 1000 ? 1000 : 100));
// Open the output file.
String sFilename = FileUtil.FormatFilename("SaBusMgr_CommodityExport_" + DateUtil.Format(DateTime.Now,"yyyyMMddHHmmss") + ".csv");
String sFilename = FileUtil.FormatFilename("SaBusMgr_CommodityExport_" + DateUtil.Format(DateTime.Now, "yyyyMMddHHmmss") + ".csv");
String sOutputPath = interfaceParams.OutputFolder + FileUtil.GetPathDelim(interfaceParams.OutputFolder) + sFilename;
using (StreamWriter textWriter = new StreamWriter(sFilename, false))
{
@@ -390,8 +390,8 @@ namespace Sarand.BusinessMgr.Net.Core.Procs.Interfaces
SetProgress(progressPrefix + "\nQuerying data");
using (DbResultSet resultSet = ExecuteQuery())
{
if (resultSet.HasErrorState()) return SetProcessError(resultSet.ErrorState.Message);
if (resultSet == null) return SetProcessError("Failed to query commodity data");
if (resultSet.HasErrorState()) return AddErrorMessage(resultSet.ErrorState.Message);
if (resultSet == null) return AddErrorMessage("Failed to query commodity data");
while (OK && resultSet != null && resultSet.NextRecord())
{
Params.ItemBeingExported++;
@@ -416,7 +416,7 @@ namespace Sarand.BusinessMgr.Net.Core.Procs.Interfaces
catch (Exception exc)
{
SaLogger.Warn(fn + " " + ExceptionUtil.GetLogMessage(exc));
return SetProcessError(ExceptionUtil.GetErrorStateMessage(exc));
return AddErrorMessage(ExceptionUtil.GetErrorStateMessage(exc));
}
return OK;
}
@@ -424,7 +424,7 @@ namespace Sarand.BusinessMgr.Net.Core.Procs.Interfaces
//-------------------------------------------------------------------
// E x p o r t T o E x c e l
//-------------------------------------------------------------------
private bool ExportCommodityData_Excel ()
private bool ExportCommodityData_Excel()
{
String fn = this.GetFn("ExportCommodityData_Excel");
if (!OK) return false;
@@ -438,14 +438,15 @@ namespace Sarand.BusinessMgr.Net.Core.Procs.Interfaces
using (SaMsSpreadsheet spreadsheet = new SaMsSpreadsheet_POI())
{
spreadsheet.CreateWorkbook();
SaMsExcelStyleAttr attrSheet = new SaMsExcelStyleAttr();
attrSheet.FontAttr.Name = interfaceParams.Font_Name;
attrSheet.FontAttr.HeightInPoints = interfaceParams.Font_Size_Data;
spreadsheet.ApplyDefaultStyle(attrSheet);
spreadsheet.SetWorksheetCount(1);
OpenWorkSheet(spreadsheet, 1, "Commodities");
SaMsExcelItemAttr attrSheet = new SaMsExcelItemAttr();
attrSheet.FontName = interfaceParams.Font_Name;
attrSheet.FontSize = interfaceParams.Font_Size_Data;
spreadsheet.SetWorksheetAttr(attrSheet);
// Margins.
SaMsExcelPageSetup_Config cfgPageSetup = new SaMsExcelPageSetup_Config();
cfgPageSetup.Unit = SaExcelLogicalUnit.Cm;
@@ -462,14 +463,14 @@ namespace Sarand.BusinessMgr.Net.Core.Procs.Interfaces
{
SetProgress(progressPrefix + "\nPopulating column headers");
SaMsExcelColumnHeading_Config cfgHeadings = new SaMsExcelColumnHeading_Config();
cfgHeadings.HeadingFontSize = interfaceParams.Font_Size_Header;
cfgHeadings.FirstHeadingRow = Params.FirstDataRow;
cfgHeadings.FontAttr.HeightInPoints = interfaceParams.Font_Size_Header;
cfgHeadings.FirstHeadingRowPos = Params.FirstDataRow;
int colPos_Code = interfaceParams.Columns.GetPosForId(SbmCommodity.Column.Code);
if (colPos_Code > 0)
Params.FreezeColumn = (uint)(colPos_Code + Params.FirstDataColumn);
cfgHeadings.FreezeColumn = Params.FreezeColumn;
cfgHeadings.FreezeColPos = Params.FreezeColumn;
if (interfaceParams.BackgroundColour_Header != -1)
cfgHeadings.InteriorColour = interfaceParams.BackgroundColour_Header;
cfgHeadings.ForegroundColourRGB = interfaceParams.BackgroundColour_Header;
spreadsheet.SetColumnHeadings(interfaceParams.Columns, cfgHeadings);
Params.FirstDataRow++;
}
@@ -488,28 +489,28 @@ namespace Sarand.BusinessMgr.Net.Core.Procs.Interfaces
catch (Exception exc)
{
SaLogger.Warn(fn + " " + ExceptionUtil.GetLogMessage(exc));
return SetProcessError(ExceptionUtil.GetErrorStateMessage(exc));
return AddErrorMessage(ExceptionUtil.GetErrorStateMessage(exc));
}
return OK;
}
//-------------------------------------------------------------------
private bool ExportDataToExcel (SaMsSpreadsheet spreadsheet, String progressPrefix)
private bool ExportDataToExcel(SaMsSpreadsheet spreadsheet, String progressPrefix)
{
if (!OK) return false;
String dataName_Code = CommodityMgr.GetCommodityColumnDfn(SbmCommodity.Column.Code).DataName;
// Progress is refreshed every few rows.
Params.CommodityCountTotal = CommodityMgr.GetCommodityCount(GetParams());
int progressRefreshRows = (int)Math.Max(5,Params.CommodityCountTotal/(Params.CommodityCountTotal>1000?1000:100));
int progressRefreshRows = (int)Math.Max(5, Params.CommodityCountTotal / (Params.CommodityCountTotal > 1000 ? 1000 : 100));
// Query data.
SetProgress(progressPrefix + "\nQuerying data");
using (DbResultSet resultSet = ExecuteQuery())
{
if (resultSet.HasErrorState()) return SetProcessError(resultSet.ErrorState.Message);
if (resultSet == null) return SetProcessError("Failed to query commodity data");
if (resultSet.HasErrorState()) return AddErrorMessage(resultSet.ErrorState.Message);
if (resultSet == null) return AddErrorMessage("Failed to query commodity data");
while (OK && resultSet != null && resultSet.NextRecord())
{
Params.ItemBeingExported++;
@@ -544,50 +545,50 @@ namespace Sarand.BusinessMgr.Net.Core.Procs.Interfaces
case SbmCommodity.Column.Id:
break;
case SbmCommodity.Column.Image:
{
// Retrieve the image path.
String sImagePath = GetDataValue(columnDfn, values).ToString();
// Configure image.
SaMsExcelImage_Config cfgImg = new SaMsExcelImage_Config();
cfgImg.ImagePath = CommodityMgr.GetActualImagePath(sImagePath);
cfgImg.ColPos = colPos;
cfgImg.RowPos = rowPos;
cfgImg.Unit = SaExcelLogicalUnit.Cm;
if (FileUtil.IsFile(cfgImg.ImagePath))
{
if (interfaceParams.DataRowHeightCm > 0)
cfgImg.TargetHeight = interfaceParams.DataRowHeightCm;
spreadsheet.InsertImage(cfgImg);
// Retrieve the image path.
String sImagePath = GetDataValue(columnDfn, values).ToString();
// Configure image.
SaMsExcelImage_Config cfgImg = new SaMsExcelImage_Config();
cfgImg.ImagePath = CommodityMgr.GetActualImagePath(sImagePath);
cfgImg.ColPos = colPos;
cfgImg.RowPos = rowPos;
cfgImg.Unit = SaExcelLogicalUnit.Cm;
if (FileUtil.IsFile(cfgImg.ImagePath))
{
if (interfaceParams.DataRowHeightCm > 0)
cfgImg.TargetHeight = interfaceParams.DataRowHeightCm;
spreadsheet.InsertImage(cfgImg);
}
break;
}
break;
}
case SbmCommodity.Column.BarcodeImage:
{
SaMsExcelImage_Config cfgImg = new SaMsExcelImage_Config();
cfgImg.ImagePath = CommodityMgr.GetBarcodeImagePath(commodityCode);
cfgImg.ColPos = colPos;
cfgImg.RowPos = rowPos;
cfgImg.Unit = SaExcelLogicalUnit.Cm;
if (FileUtil.IsFile(cfgImg.ImagePath))
{
if (interfaceParams.DataRowHeightCm > 0)
cfgImg.TargetHeight = interfaceParams.DataRowHeightCm;
spreadsheet.InsertImage(cfgImg);
SaMsExcelImage_Config cfgImg = new SaMsExcelImage_Config();
cfgImg.ImagePath = CommodityMgr.GetBarcodeImagePath(commodityCode);
cfgImg.ColPos = colPos;
cfgImg.RowPos = rowPos;
cfgImg.Unit = SaExcelLogicalUnit.Cm;
if (FileUtil.IsFile(cfgImg.ImagePath))
{
if (interfaceParams.DataRowHeightCm > 0)
cfgImg.TargetHeight = interfaceParams.DataRowHeightCm;
spreadsheet.InsertImage(cfgImg);
}
break;
}
break;
}
default:
{
cellValue = GetDataValue(columnDfn, values);
if (!columnDfn.ShowZeroValues && cellValue.IsNumeric() && cellValue.ToNumeric() == 0.0)
cellValue = new DataValue(); // Blank value.
break;
}
{
cellValue = GetDataValue(columnDfn, values);
if (!columnDfn.ShowZeroValues && cellValue.IsNumeric() && cellValue.ToNumeric() == 0.0)
cellValue = new DataValue(); // Blank value.
break;
}
}
cellValues.Add(cellValue);
}
// Populate values to spreadesheet row.
spreadsheet.SetRangeFormulas(new SaMsExcelRange(Params.FirstDataColumn, rowPos, (uint)(Params.FirstDataColumn + cellValues.Count - 1), rowPos), cellValues.ToObjectList());
spreadsheet.SetRangeValues(new SaMsExcelRange(Params.FirstDataColumn, rowPos, (uint)(Params.FirstDataColumn + cellValues.Count - 1), rowPos), cellValues);
IncrementStatistic(ProcessingStatistic.CommoditiesExported);
//if (Params.ItemBeingExported >= 10) break;
}
@@ -600,7 +601,7 @@ namespace Sarand.BusinessMgr.Net.Core.Procs.Interfaces
// P r o g r e s s I n f o r m a t i o n
//-------------------------------------------------------------------
// Display progress information - redirected to dialog:
protected void SetProgress (String message)
protected void SetProgress(String message)
{
// Progress message:
List<String> progressMessages = new List<string>();
@@ -620,7 +621,7 @@ namespace Sarand.BusinessMgr.Net.Core.Procs.Interfaces
//-------------------------------------------------------------------
// I n t e r f a c e I t e m O p t i o n s
//-------------------------------------------------------------------
public List<SaItemOptionDfn> GetInterfaceItemOptionDfns ()
public List<SaItemOptionDfn> GetInterfaceItemOptionDfns()
{
InitOptionDefinitions();
List<SaItemOptionDfn> interfaceOptionDfns = new List<SaItemOptionDfn>();
@@ -648,7 +649,7 @@ namespace Sarand.BusinessMgr.Net.Core.Procs.Interfaces
}
//-------------------------------------------------------------------
private void InitOptionDefinitions ()
private void InitOptionDefinitions()
{
if (optionContexts_Value == null)
{
@@ -1019,7 +1020,7 @@ namespace Sarand.BusinessMgr.Net.Core.Procs.Interfaces
}
}
}
private bool AddColumnValue (SbmCommodity.Column eCol, String sCode, String sDesc)
private bool AddColumnValue(SbmCommodity.Column eCol, String sCode, String sDesc)
{
return AddColumnValue(eCol, sCode, sDesc, null, null, null);
}
@@ -1073,7 +1074,7 @@ namespace Sarand.BusinessMgr.Net.Core.Procs.Interfaces
Export_Commodities,
}
/// <summary>Configure processing options.</summary>
public static List<SaProcess_Base.GroupData> GetProcessingOptions (ISbmUserContext userContext, SaInterface interfaceObj)
public static List<SaProcess_Base.GroupData> GetProcessingOptions(ISbmUserContext userContext, SaInterface interfaceObj)
{
List<SaProcess_Base.GroupData> optionGroups = new List<SaProcess_Base.GroupData>();
@@ -1088,7 +1089,7 @@ namespace Sarand.BusinessMgr.Net.Core.Procs.Interfaces
//-------------------------------------------------------------------
/// <summary>Process interface using selected options.</summary>
public static SbmInterfaceProcess ConfigureProcess (ISbmUserContext userContext, SaInterface interfaceObj, SaProcess_Base.GroupData processGroup)
public static SbmInterfaceProcess ConfigureProcess(ISbmUserContext userContext, SaInterface interfaceObj, SaProcess_Base.GroupData processGroup)
{
if (processGroup.OptionGroup.Type == (int)InterfaceProcessGroup.Export)
{
@@ -81,7 +81,7 @@ namespace Sarand.BusinessMgr.Net.Core.Procs.Procs
catch (Exception exc)
{
SaLogger.Warn(fn + " " + exc.GetType().Name + ": " + exc.Message, true);
return SetProcessError("Unable to connect to FTP host");
return AddErrorMessage("Unable to connect to FTP host");
}
finally
{
@@ -38,7 +38,7 @@ namespace Sarand.BusinessMgr.Net.Core.Procs.Procs
}
public new class Config : SaProcess.Config
{
protected Config(ISbmUserContext userContext) : base()
protected Config(ISbmUserContext userContext) : base(userContext.DbUpdateInfo)
{
UserContext = userContext;
}
@@ -221,7 +221,7 @@ namespace Sarand.BusinessMgr.Net.Core.Procs.Procs
// ToDo Save Folder...
String saveFolder = ""; //GetOption_SaveFolder();
if (OK && !StringUtil.IsEmpty(saveFolder) && !DirectoryUtil.DirectoryExists(saveFolder))
SetProcessError("Invalid output folder specified.");
AddErrorMessage("Invalid output folder specified.");
// Perform export.
if (OK)
@@ -248,11 +248,11 @@ namespace Sarand.BusinessMgr.Net.Core.Procs.Procs
if (!OK) return false;
// Validate environment.
if (CommodityMgr == null) return SetProcessError("Unable to initialise commodity manager");
if (ProcessMgr == null) return SetProcessError("Unable to initialise process manager");
if (ReferenceDataConsole == null) return SetProcessError("Reference data console not available");
if (CommodityMgr == null) return AddErrorMessage("Unable to initialise commodity manager");
if (ProcessMgr == null) return AddErrorMessage("Unable to initialise process manager");
if (ReferenceDataConsole == null) return AddErrorMessage("Reference data console not available");
if (config.DataFormat != DataFormatType.Excel) return SetProcessError("Only Excel export format supported.");
if (config.DataFormat != DataFormatType.Excel) return AddErrorMessage("Only Excel export format supported.");
// Data field columns.
Params.IncludeCol_MaterialCost = StringUtil.ToBool(GetProcessingOptionValue(OptionName_Flag, optionContexts_Flag.GetCodeForType(ProcessOptionFlag.IncludeCol_MaterialCost)));
@@ -293,14 +293,14 @@ namespace Sarand.BusinessMgr.Net.Core.Procs.Procs
// Headings.
SetProgressInfo(progressPrefix + " - Populating headings");
SaMsExcelColumnHeading_Config configHeadings = new SaMsExcelColumnHeading_Config();
configHeadings.HeadingFontSize = 10;
configHeadings.FirstHeadingRow = 1;
configHeadings.InteriorColour = ColorTranslator.ToOle(ExcelColumnHeadingColor);
configHeadings.FontAttr.HeightInPoints = 10;
configHeadings.FirstHeadingRowPos = 1;
configHeadings.ForegroundColourRGB = ExcelColumnHeadingColor.ToArgb();
configHeadings.BorderWeight = SaXlBorderWeight.Thin;
configHeadings.FreezeColumn = (uint)(displayColumns.GetPosForId(SbmCommodity.Column.Code) + 1);
configHeadings.FreezeColPos = (uint)(displayColumns.GetPosForId(SbmCommodity.Column.Code) + 1);
spreadsheet.SetColumnHeadings(displayColumns, configHeadings);
Params.HomeColumn = configHeadings.FreezeColumn + 1;
Params.HomeColumn = configHeadings.FreezeColPos + 1;
Params.HomeRow = Params.FirstDataRow;
ExportCommoditiesToExcel(spreadsheet);
@@ -315,7 +315,7 @@ namespace Sarand.BusinessMgr.Net.Core.Procs.Procs
catch (Exception exc)
{
SaLogger.Error(fn + " " + ExceptionUtil.GetLogMessage(exc));
return SetProcessError(exc.GetType().Name + " exporting data to Excel.");
return AddErrorMessage(exc.GetType().Name + " exporting data to Excel.");
}
return OK;
@@ -625,8 +625,8 @@ namespace Sarand.BusinessMgr.Net.Core.Procs.Procs
using (DbResultSet resultSet = CommodityMgr.ExecuteCommodityQuery(queryColumns, config.CommodityParams))
{
if (resultSet.HasErrorState()) return SetProcessError(resultSet.ErrorState.Message);
if (resultSet == null) return SetProcessError("Failed to query commodity data");
if (resultSet.HasErrorState()) return AddErrorMessage(resultSet.ErrorState.Message);
if (resultSet == null) return AddErrorMessage("Failed to query commodity data");
while (OK && resultSet != null && resultSet.NextRecord())
{
if (++commodityCount % progressRefreshRows == 0 || commodityCount == 1)
@@ -670,7 +670,7 @@ namespace Sarand.BusinessMgr.Net.Core.Procs.Procs
for (int columnIdx = 0; columnIdx < displayColumns.Count; columnIdx++)
{
ColumnDfn column = displayColumns[columnIdx];
SaMsExcelItemAttr attrCell = new SaMsExcelItemAttr();
SaMsExcelStyleAttr attrCell = new SaMsExcelStyleAttr();
bool cellAttributesPopulated = false;
// Intercept specific values.
@@ -687,8 +687,8 @@ namespace Sarand.BusinessMgr.Net.Core.Procs.Procs
if (SacsSystem.GetStatusForCode(statusCode) != SaStatus.Current)
{
value = new DataValue("X");
attrCell.Bold = true;
attrCell.FontColourIndex = SaXlColourIndex.Red;
attrCell.FontAttr.Bold = true;
attrCell.FontAttr.ColourIndex = SaXlColourIndex.Red;
cellAttributesPopulated = true;
}
}
@@ -705,8 +705,8 @@ namespace Sarand.BusinessMgr.Net.Core.Procs.Procs
value = new DataValue(availableStock);
if (availableStock < -0.001m)
{
attrCell.Bold = true;
attrCell.FontColourIndex = SaXlColourIndex.Red; //38; // Pale red (pink).
attrCell.FontAttr.Bold = true;
attrCell.FontAttr.ColourIndex = SaXlColourIndex.Red; //38; // Pale red (pink).
cellAttributesPopulated = true;
}
}
@@ -744,9 +744,9 @@ namespace Sarand.BusinessMgr.Net.Core.Procs.Procs
decimal imminentAvailablePropReorder = (imminentAvailable * 100.0m) / reorderLevel;
value = new DataValue(imminentAvailablePropReorder);
if (imminentAvailablePropReorder < ReorderPercRed)
attrCell.InteriorColourIndex = SaXlColourIndex.Rose;
attrCell.ForegroundColourIndex = SaXlColourIndex.Rose;
else if (imminentAvailablePropReorder < ReorderPercGreen)
attrCell.InteriorColourIndex = SaXlColourIndex.Tan;
attrCell.ForegroundColourIndex = SaXlColourIndex.Tan;
cellAttributesPopulated = true;
}
}
@@ -771,9 +771,9 @@ namespace Sarand.BusinessMgr.Net.Core.Procs.Procs
decimal percSoldVsBudget = salesProjPeriod > 0.0m ? stockSoldPeriod / salesProjPeriod * 100.0m : 0.0m;
value = new DataValue(stockSoldPeriod);
if (percSoldVsBudget < BudgetPercRed)
attrCell.InteriorColourIndex = SaXlColourIndex.Rose;
attrCell.ForegroundColourIndex = SaXlColourIndex.Rose;
else if (percSoldVsBudget < BudgetPercGreen)
attrCell.InteriorColourIndex = SaXlColourIndex.Tan;
attrCell.ForegroundColourIndex = SaXlColourIndex.Tan;
cellAttributesPopulated = true;
}
}
@@ -803,9 +803,9 @@ namespace Sarand.BusinessMgr.Net.Core.Procs.Procs
// Check month end stock level against reorder level.
if (projSales.EndOfMonth < 0.0m)
attrCell.InteriorColourIndex = SaXlColourIndex.Rose;
attrCell.ForegroundColourIndex = SaXlColourIndex.Rose;
else if (reorderLevel > 0.0m && projSales.EndOfMonth <= reorderLevel)
attrCell.InteriorColourIndex = SaXlColourIndex.Tan;
attrCell.ForegroundColourIndex = SaXlColourIndex.Tan;
cellAttributesPopulated = true;
}
}
@@ -837,16 +837,16 @@ namespace Sarand.BusinessMgr.Net.Core.Procs.Procs
cellValues.Add(value);
// Populate the cell's attributes.
if (attrCell.InteriorColourIndex != SaXlColourIndex.NotSpecified)
if (attrCell.ForegroundColourIndex != SaXlColourIndex.NotSpecified)
{
attrCell.InteriorPattern = SaXlPattern.Solid;
attrCell.InteriorPatternColourIndex = SaXlColourIndex.Automatic;
attrCell.ForegroundColourIndex = SaXlColourIndex.Automatic;
cellAttributesPopulated = true;
}
if (cellAttributesPopulated)
spreadsheet.SetRangeAttr(new SaMsExcelRange(Params.FirstDataColumn + (uint)columnIdx, iRow), attrCell);
}
spreadsheet.SetRangeFormulas(new SaMsExcelRange(Params.FirstDataColumn, iRow, Params.FirstDataColumn + (uint)cellValues.Count - 1, iRow), cellValues.ToObjectList());
spreadsheet.SetRangeValues(new SaMsExcelRange(Params.FirstDataColumn, iRow, Params.FirstDataColumn + (uint)cellValues.Count - 1, iRow), cellValues);
IncrementStatistic(ProcessingStatistic.CommoditiesExported);
}
}
+13 -17
View File
@@ -1,23 +1,19 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netcoreapp3.1</TargetFramework>
<RootNamespace>Sarand.BusinessMgr.Net.Core</RootNamespace>
</PropertyGroup>
<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
<RootNamespace>Sarand.BusinessMgr.Net.Core</RootNamespace>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\..\..\Common-VS\Workspace\SacsStandardBase\SacsStandardBase.csproj" />
<ProjectReference Include="..\..\..\Common-VS\Workspace\SacsStandardProc\SacsStandardProc.csproj" />
<ProjectReference Include="..\SaBusMgrStandardBase\SaBusMgrStandardBase.csproj" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\..\Common-VS\Workspace\SacsStandardBase\SacsStandardBase.csproj" />
<ProjectReference Include="..\..\..\Common-VS\Workspace\SacsStandardProc\SacsStandardProc.csproj" />
<ProjectReference Include="..\SaBusMgrStandardBase\SaBusMgrStandardBase.csproj" />
</ItemGroup>
<ItemGroup>
<Folder Include="Procs\DataHelpers\" />
<Folder Include="Procs\Utils\" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="System.Drawing.Common" Version="5.0.2" />
</ItemGroup>
<ItemGroup>
<Folder Include="Procs\DataHelpers\" />
<Folder Include="Procs\Utils\" />
</ItemGroup>
</Project>
+18 -18
View File
@@ -1,25 +1,25 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netcoreapp3.1</TargetFramework>
<RootNamespace>Sarand.BusinessMgr.Net.Core</RootNamespace>
</PropertyGroup>
<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
<RootNamespace>Sarand.BusinessMgr.Net.Core</RootNamespace>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.Mvc.Razor.RuntimeCompilation" Version="3.1.16" />
</ItemGroup>
<ItemGroup>
<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="..\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="..\SaBusMgrStandardBase\SaBusMgrStandardBase.csproj" />
<ProjectReference Include="..\SaBusMgrStandardDbas\SaBusMgrStandardDbas.csproj" />
</ItemGroup>
<ItemGroup>
<Folder Include="Web\Core\" />
</ItemGroup>
<ItemGroup>
<Folder Include="Web\Core\" />
</ItemGroup>
</Project>
+67 -34
View File
@@ -38,61 +38,87 @@ namespace Sarand.BusinessMgr.Net.Core.Web.App
if (configSection != null)
{
// Environments.
foreach (ConfigurationSection environment in configSection.GetSection("Environments").GetChildren())
environments.Add(StringUtil.TrimAll(environment.Value));
foreach (ConfigurationSection environmentSection in configSection.GetSection("Environments").GetChildren())
environments.Add(StringUtil.TrimAll(environmentSection.Value));
Instance = StringUtil.OnEmpty(configSection["Instance"], Instance);
// Security.
IConfigurationSection security = configSection.GetSection("Security");
if (security.Exists())
IConfigurationSection securitySection = configSection.GetSection("Security");
if (securitySection.Exists())
{
// Security - Password.
IConfigurationSection securityPassword = security.GetSection("Password");
if (securityPassword.Exists())
IConfigurationSection securityPasswordSection = securitySection.GetSection("Password");
if (securityPasswordSection.Exists())
{
Security.Password.MinLength = StringUtil.ToInt(securityPassword["MinLength"], Security.Password.MinLength);
Security.Password.MinLength = StringUtil.ToInt(securityPasswordSection["MinLength"], Security.Password.MinLength);
}
// Security - Sign-Up.
IConfigurationSection securitySignUp = security.GetSection("SignUp");
if (securitySignUp.Exists())
IConfigurationSection securitySignUpSection = securitySection.GetSection("SignUp");
if (securitySignUpSection.Exists())
{
}
// Security - Sign-In.
IConfigurationSection securitySignIn = security.GetSection("SignIn");
if (securitySignIn.Exists())
IConfigurationSection securitySignInSection = securitySection.GetSection("SignIn");
if (securitySignInSection.Exists())
{
IConfigurationSection keepLoggedIn = securitySignIn.GetSection("SignIn");
if (keepLoggedIn.Exists())
IConfigurationSection keepLoggedInSection = securitySignInSection.GetSection("SignIn");
if (keepLoggedInSection.Exists())
{
Security.SignIn.KeepLoggedIn.Allowed = StringUtil.ToBool(StringUtil.OnEmpty(keepLoggedIn["Allowed"], Security.SignIn.KeepLoggedIn.Allowed.ToString()));
Security.SignIn.KeepLoggedIn.CookieExpiresDays = StringUtil.ToInt(keepLoggedIn["CookieExpiresDays"], Security.SignIn.KeepLoggedIn.CookieExpiresDays);
Security.SignIn.KeepLoggedIn.Allowed = StringUtil.ToBool(StringUtil.OnEmpty(keepLoggedInSection["Allowed"], Security.SignIn.KeepLoggedIn.Allowed.ToString()));
Security.SignIn.KeepLoggedIn.CookieExpiresDays = StringUtil.ToInt(keepLoggedInSection["CookieExpiresDays"], Security.SignIn.KeepLoggedIn.CookieExpiresDays);
}
}
}
// Email.
IConfigurationSection email = configSection.GetSection("Email");
if (email.Exists())
IConfigurationSection emailSection = configSection.GetSection("Email");
if (emailSection.Exists())
{
Email.Context = email["Context"];
PopulateEmailConfig(Email.ContactUs, email.GetSection("ContactUs"));
PopulateEmailConfig(Email.Signup_Activate, email.GetSection("Signup").GetSection("Activate"));
PopulateEmailConfig(Email.Signup_Welcome, email.GetSection("Signup").GetSection("Welcome"));
Email.Context = emailSection["Context"];
PopulateEmailConfig(Email.ContactUs, emailSection.GetSection("ContactUs"));
PopulateEmailConfig(Email.Signup_Activate, emailSection.GetSection("Signup").GetSection("Activate"));
PopulateEmailConfig(Email.Signup_Welcome, emailSection.GetSection("Signup").GetSection("Welcome"));
}
// Documents.
IConfigurationSection documents = configSection.GetSection("Documents");
if (documents.Exists())
IConfigurationSection documentsSection = configSection.GetSection("Documents");
if (documentsSection.Exists())
{
Documents.RootFolder = StringUtil.OnEmpty(documents["RootFolder"], Documents.RootFolder);
Documents.RootFolder = StringUtil.OnEmpty(documentsSection["RootFolder"], Documents.RootFolder);
}
// Logging.
IConfigurationSection logging = configSection.GetSection("Logging");
if (logging.Exists())
IConfigurationSection loggingSection = configSection.GetSection("Logging");
if (loggingSection.Exists())
{
Logging.RootFolder = StringUtil.OnEmpty(logging["RootFolder"], Logging.RootFolder);
Logging.RootFolder = StringUtil.OnEmpty(loggingSection["RootFolder"], Logging.RootFolder);
}
// Databases.
extractDatabaseConfig(ControlDatabase, configSection.GetSection("ControlDatabase"));
extractDatabaseConfig(BusMgrDatabase, configSection.GetSection("BusMgrDatabase"));
extractDatabaseConfig(TemplateDatabase, configSection.GetSection("TemplateDatabase"));
// Control database.
IConfigurationSection controlDatabaseSection = configSection.GetSection("ControlDatabase");
if (controlDatabaseSection.Exists())
{
ControlDatabase = new DatabaseConfig();
extractDatabaseConfig(ControlDatabase, controlDatabaseSection);
}
// Template database.
IConfigurationSection templateDatabaseSection = configSection.GetSection("TemplateDatabase");
if (templateDatabaseSection.Exists())
{
TemplateDatabase = new DatabaseConfig();
extractDatabaseConfig(TemplateDatabase, templateDatabaseSection);
}
// BusinessMgr databases.
IConfigurationSection busMgrDatabasesSection = configSection.GetSection("BusMgrDatabases");
if (busMgrDatabasesSection.Exists())
{
foreach (IConfigurationSection busMgrDbSection in busMgrDatabasesSection.GetChildren())
{
SbmAppConfig.DatabaseConfig dbConfig = new SbmAppConfig.DatabaseConfig();
extractDatabaseConfig(dbConfig, busMgrDbSection);
BusMgrDatabases.Add(dbConfig);
}
}
// Variants.
foreach (ConfigurationSection variantSection in configSection.GetSection("Variants").GetChildren())
{
@@ -117,6 +143,12 @@ namespace Sarand.BusinessMgr.Net.Core.Web.App
private void extractDatabaseConfig(DatabaseConfig database, IConfigurationSection databaseSection)
{
if (!databaseSection.Exists()) return;
IConfigurationSection identifiersSection = databaseSection.GetSection("Identifiers");
if (identifiersSection.Exists())
{
foreach (var identifier in identifiersSection.GetChildren())
database.Identifiers.Add(StringUtil.TrimAll(identifier.Value));
}
database.Location = StringUtil.OnEmpty(databaseSection["Location"], database.Location);
database.User = StringUtil.OnEmpty(databaseSection["User"], database.User);
database.Password = StringUtil.OnEmpty(databaseSection["Password"], database.Password);
@@ -179,13 +211,14 @@ namespace Sarand.BusinessMgr.Net.Core.Web.App
// Databases.
public class DatabaseConfig
{
public List<String> Identifiers { get; } = new List<string>();
public String Location { get; internal set; }
public String User { get; internal set; }
public String Password { get; internal set; }
}
public DatabaseConfig ControlDatabase { get; } = new DatabaseConfig();
public DatabaseConfig BusMgrDatabase { get; } = new DatabaseConfig();
public DatabaseConfig TemplateDatabase { get; } = new DatabaseConfig();
public DatabaseConfig ControlDatabase { get; private set; }
public DatabaseConfig TemplateDatabase { get; private set; }
public List<DatabaseConfig> BusMgrDatabases { get; } = new List<DatabaseConfig>();
// Documents.
public class DocumentsConfig
{
@@ -68,7 +68,8 @@ namespace Sarand.BusinessMgr.Net.Core.Web.App
applicationParams.Add("Configuration", SbmSystem.DebugMode ? "Debug" : "Release");
applicationParams.Add("Environment", EnvironmentName);
applicationParams.Add("Control database", AppConfig.ControlDatabase.Location);
applicationParams.Add("BusMgr database", AppConfig.BusMgrDatabase.Location);
foreach (SbmAppConfig.DatabaseConfig dbConfig in AppConfig.BusMgrDatabases)
applicationParams.Add("BusMgr database", (dbConfig.Identifiers.Any() ? $"[{String.Join(",", dbConfig.Identifiers)}] " : "") + dbConfig.Location);
applicationParams.Add("Application root", SbmSystem.ApplicationFolder);
if (!StringUtil.IsEmpty(AppConfig.Documents.RootFolder))
applicationParams.Add("Documents root", AppConfig.Documents.RootFolder);
+15 -9
View File
@@ -20,16 +20,22 @@ namespace Sarand.BusinessMgr.Net.Core.Web.App
{
securityProfile = new SbmSecurityProfile(this, false);
}
// Protected construction.
protected SbmSessionData_Web(SbmSessionContext sessionContext, SaSecurityItem securityItemUser, SbmAffiliate affiliateUser, List<SysIdString> supervisedOrganisations, SbmAssociation associationOrganisation, SbmAffiliate affiliateOrganisation) :
base(sessionContext, securityItemUser, affiliateUser, supervisedOrganisations, associationOrganisation, affiliateOrganisation)
{
securityProfile = new SbmSecurityProfile(this, false);
}
// Protected construction.
protected SbmSessionData_Web(SbmSessionContext sessionContext) :
base(sessionContext)
{
securityProfile = new SbmSecurityProfile(this, false);
}
// Protected construction.
protected SbmSessionData_Web(SbmSessionContext sessionContext, SaSecurityItem securityItemUser, SbmAffiliate affiliateUser, List<SysIdString> supervisedOrganisations, SbmAssociation associationOrganisation, SbmAffiliate affiliateOrganisation) :
base(sessionContext, securityItemUser, affiliateUser, supervisedOrganisations, associationOrganisation, affiliateOrganisation)
{
securityProfile = new SbmSecurityProfile(this, false);
}
//-------------------------------------------------------------------
// Security Profile.
private readonly SbmSecurityProfile securityProfile;
//-------------------------------------------------------------------
// Security Profile.
private readonly SbmSecurityProfile securityProfile;
public ISbmSecurityProfile SecurityProfile { get { return securityProfile; } }
//-------------------------------------------------------------------
@@ -9,7 +9,7 @@ namespace Sarand.BusinessMgr.Net.Core.Web.App
public class SbmUserContext_Web : SbmUserContext
{
//-------------------------------------------------------------------
// Data memebrs.
// Data members.
//-------------------------------------------------------------------
// Construction.
@@ -6,6 +6,7 @@ using Microsoft.AspNetCore.Routing;
using Microsoft.Net.Http.Headers;
using Newtonsoft.Json;
using Newtonsoft.Json.Serialization;
using Sacs.Web.Components;
using Sarand.BusinessMgr.Net.Core.Web.App;
using Sarand.BusinessMgr.Net.Core.Web.Models;
using Sarand.BusinessMgr.Net.Standard.Base.App;
@@ -40,7 +41,7 @@ using System.Web;
namespace Sarand.BusinessMgr.Net.Core.Web.Controllers
{
//=======================================================================
public abstract class SbmController_Base : Controller
public abstract class SbmController_Base : SaController
{
//-------------------------------------------------------------------
// Data members.
@@ -67,27 +68,11 @@ namespace Sarand.BusinessMgr.Net.Core.Web.Controllers
//-------------------------------------------------------------------
// Properties.
public bool DebugMode { get; } = SacsSystem.DebugMode;
protected SbmApplication_Web App { get { return SbmGlobals_Web.Application; } }
public SbmAppConfig AppConfig { get { return App.AppConfig; } }
public SbmSessionContext SessionContext { get { return SessionData?.SessionContext ?? null; } }
public ISbmUserContext UserContext { get { return SessionData; } }
protected SbmLocaliser Localiser { get; }
/// <summary>
/// Indicate whether session data should be stored by action.
/// <para>Any action that might modify the session data must store it.</para>
/// <para>Some actions such as scoring a password's complexity don't need to.</para>
/// </summary>
public virtual bool StoreSessionData { get; protected set; } = true;
// Culture.
private string LanguageIso2
{
get
{
var feature = HttpContext.Features.Get<IRequestCultureFeature>();
return feature.RequestCulture.UICulture.TwoLetterISOLanguageName.ToLower();
}
}
// Business components.
public SbmInternals Internals { get { if (internals == null) internals = new SbmInternals(UserContext); return internals; } }
// Managers.
@@ -119,36 +104,6 @@ namespace Sarand.BusinessMgr.Net.Core.Web.Controllers
public SaEmailConfig EmailConfig { get { return Internals.EmailConfig; } }
public SbmEmailConsole EmailConsole { get { return Internals.EmailConsole; } }
//-------------------------------------------------------------------
// Redirection locations.
//-------------------------------------------------------------------
public static readonly String ParamName_SessionExpired = "SessionExpired";
protected enum RedirectLocation
{
WebAppBase,
WebAppHome,
WebAppHome_SessionExpired,
}
protected String GetRedirectLocation(RedirectLocation redirectLocation)
{
string protocol = Request.Scheme;
var parameters = new RouteValueDictionary();
String homeAction = Url.Action("Index", "Home", parameters, protocol);
switch (redirectLocation)
{
case RedirectLocation.WebAppBase:
return Regex.Replace(homeAction, "\\/$", "") + "/";
case RedirectLocation.WebAppHome:
return homeAction;
case RedirectLocation.WebAppHome_SessionExpired:
// Set the session expired parameter so that the page can report it.
// NB Paramater must have a value to be included in the URL query string.
parameters.Add(ParamName_SessionExpired, "1");
return homeAction;
}
return "";
}
//-------------------------------------------------------------------
// Action execution events.
//-------------------------------------------------------------------
@@ -205,6 +160,7 @@ namespace Sarand.BusinessMgr.Net.Core.Web.Controllers
if (model == null) return;
// Session data.
model.Controller = this;
model.SessionData_Web = SessionData;
// Redirect locations.
@@ -213,73 +169,6 @@ namespace Sarand.BusinessMgr.Net.Core.Web.Controllers
model.RedirectLocation.Home_SessionExpired = GetRedirectLocation(RedirectLocation.WebAppHome_SessionExpired);
}
//-------------------------------------------------------------------
// Parameters.
//-------------------------------------------------------------------
public String EncryptParameters(NameValueCollection parameters, String base64IV, String base64Key)
{
String fn = nameof(ControllerBase) + ".EncryptParameters";
try
{
// Copy the list of parameters, adding a random name/value pair to randomise the encryption.
NameValueCollection parametersToEncrypt = new NameValueCollection(parameters);
// NB Range 10-189 intended to have equal chance of 2 or 3 digit number.
parametersToEncrypt.Add("!" + SacsRandomHelper.GetInt32(10, 190).ToString(), SacsRandomHelper.GetInt32(10, 190).ToString());
// Sort the keys to ensure that the random '!' param is first in the query string.
String[] paramKeys = parametersToEncrypt.AllKeys;
Array.Sort(paramKeys);
// Serialise parameters as if in a URL query string - so that they can be deserialised in DecryptParameters() below.
string queryString = String.Join("&", paramKeys.Select(a => a + "=" + HttpUtility.UrlEncode(parametersToEncrypt[a])));
String encrypted = SacsEncryptionHelper.Encrypt(queryString, base64IV, base64Key);
if (DebugMode)
{
SaLogger.Debug(fn + " Encrypted: " + encrypted);
NameValueCollection decryptedParams = DecryptParameters(encrypted, base64IV, base64Key);
}
return encrypted;
}
catch (Exception exc) { SaLogger.Warn(fn + " " + ExceptionUtil.GetErrorStateMessage(exc)); }
return "";
}
public NameValueCollection DecryptParameters(String encrypted, String base64IV, String base64Key)
{
String fn = nameof(ControllerBase) + ".DecryptParameters";
if (DebugMode) SaLogger.Debug(fn + " \n\tEncrypted: " + encrypted + "\n\tIV: " + base64IV + "\n\tKey: " + base64Key);
try
{
String queryString = SacsEncryptionHelper.Decrypt(encrypted, base64IV, base64Key);
return HttpUtility.ParseQueryString(queryString);
}
catch (Exception exc) { SaLogger.Warn(fn + " " + ExceptionUtil.GetErrorStateMessage(exc)); }
return new NameValueCollection();
}
//-------------------------------------------------------------------
// Cookies.
//-------------------------------------------------------------------
/// <summary>Retrieve the value from the named request cookie.</summary>
public String GetRequestCookie(String key)
{
if (Request.Cookies.ContainsKey(key))
return Request.Cookies[key];
return null;
}
/// <summary>Set the value against the named response cookie.</summary>
public void SetResponseCookie(string key, string value, DateTime? expiresAt = null, bool isEssential = false)
{
CookieOptions options = new CookieOptions();
options.HttpOnly = true;
options.Secure = true;
options.SameSite = Microsoft.AspNetCore.Http.SameSiteMode.Strict;
if (expiresAt.HasValue)
options.Expires = expiresAt;
if (isEssential)
options.IsEssential = true;
Response.Cookies.Append(key, value, options);
}
//-------------------------------------------------------------------
// Session.
//-------------------------------------------------------------------
@@ -366,193 +255,6 @@ namespace Sarand.BusinessMgr.Net.Core.Web.Controllers
}
protected abstract SbmSessionData_Web DeserializeSessionData(String sessionDataJson);
//-------------------------------------------------------------------
// Ajax actions.
//-------------------------------------------------------------------
protected static readonly Encoding ajaxResponseEncoding = Encoding.UTF8;
protected IActionResult GetAjaxResult_GoHome(AjaxResponse_Base responseData)
{
responseData.SetGoHome();
return Json(responseData);
}
protected IActionResult GetAjaxResult_InvalidSession(AjaxResponse_Base responseData)
{
responseData.SetGoHome();
return Json(responseData);
}
//-------------------------------------------------------------------
// Base Ajax Response Data.
//-------------------------------------------------------------------
public class AjaxResponse_Base
{
// Response status.
public bool? Success { get; set; } = null;
public bool? Error { get; set; } = null;
public bool? SessionExpired { get; protected set; } = null;
public bool? GoHome { get; protected set; } = null;
public AjaxResponse_Base SetSessionExpired(String title, String text)
{
SessionExpired = true;
return SetMessage(title, text);
}
public AjaxResponse_Base SetGoHome()
{
GoHome = true;
return this;
}
public AjaxResponse_Base SetGoHome(String title, String text)
{
GoHome = true;
return SetMessage(title, text);
}
// Message - displays alert popup.
public class MessageData
{
public String Title { get; set; }
public String Text { get; set; }
}
public MessageData Message { get; private set; }
public AjaxResponse_Base SetMessage(String title, String text)
{
if (!StringUtil.IsEmpty(text))
Message = new MessageData() { Title = title, Text = text };
return this;
}
}
//-------------------------------------------------------------------
// Ajax Response Data.
//-------------------------------------------------------------------
public class AjaxResponse_Data : AjaxResponse_Base
{
//---------------------------------------------------------------
// Construction.
protected AjaxResponse_Data(SbmModel_Base model)
{
}
//---------------------------------------------------------------
/// <summary>
/// Common item response data properties.
/// <para>- Null values are ignored by the JSON serialiser.</para>
/// <para>- Return null for false boolean values where possible.</para>
/// <para>- Return null for blank string values where possible.</para>
/// </summary>
public class ItemData
{
public String Id { get; set; }
private String notes;
public String Notes { get { return StringUtil.OnEmpty(notes, null); } set { notes = StringUtil.EmptyOnNull(value).Trim(); } }
public String StatusCode { get; set; }
public String StatusDesc { get; set; }
public bool? Awaiting { get { return StatusCode.EqualsCI(SacsSystem.GetStatusCode(SaStatus.Awaiting)) ? true : (bool?)null; } }
public bool? Active { get { return StatusCode.EqualsCI(SacsSystem.GetStatusCode(SaStatus.Active)) ? true : (bool?)null; } }
public bool? Deleted { get { return StatusCode.EqualsCI(SacsSystem.GetStatusCode(SaStatus.Deleted)) ? true : (bool?)null; } }
public String CreatedOn { get; set; }
public String CreatedOnFmt { get; set; }
public String UpdatedOn { get; set; }
public String UpdatedOnFmt { get; set; }
}
//---------------------------------------------------------------
// Form data.
public class FormControlValue
{
public FormControlValue(String controlName, String value, String error)
{
ControlName = controlName;
Value = value;
Error = error;
}
public String ControlName { get; }
public String Value { get; set; }
public String Error { get; set; }
}
private readonly Dictionary<String, FormControlValue> controlValues = new Dictionary<String, FormControlValue>();
public List<FormControlValue> ControlValues { get { return controlValues.Count > 0 ? controlValues.Values.ToList() : null; } }
public void SetControlValue(String controlName, String value, String error = null)
{
if (controlValues.ContainsKey(controlName))
{
// Modify existing entry in the list.
FormControlValue controlValue = controlValues[controlName];
if (value != null) controlValue.Value = value;
if (error != null) controlValue.Error = error;
}
else
{
// Add new control to list.
controlValues.Add(controlName, new FormControlValue(controlName, value, error));
}
}
/// <summary>Assign the error to the control by name and return this response data.</summary>
public AjaxResponse_Data SetControlError(String controlName, String error)
{
return SetControlError(controlName, error, this);
}
/// <summary>
/// Assign the error to the control by name and return the specified value.
/// <para>- Allows alternative return values to be specified (eg. 'false') to avoid separate return statement in calling routine.</para>
/// </summary>
public T SetControlError<T>(String controlName, String error, T returnValue)
{
SetControlValue(controlName, null, error);
return returnValue;
}
public bool HasControlErrors()
{
foreach (FormControlValue controlValue in controlValues.Values)
{
if (!StringUtil.IsEmpty(controlValue.Error)) return true;
}
return false;
}
//---------------------------------------------------------------
// Notification - briefly displays after response received.
public class NotificationData
{
public String Title { get; set; }
public String Message { get; set; }
}
public NotificationData Notification { get; private set; }
public AjaxResponse_Data SetNotification(String title, String message)
{
Notification = new NotificationData() { Title = title, Message = message };
return this;
}
//---------------------------------------------------------------
// Paged data.
public class PagingData
{
public PagingData(SbmModel_Base model) :
this(model.Paging.PageNo, model.Paging.PageLength, model.Paging.ItemCount, model.Paging.ItemTotal)
{ }
public PagingData(uint pageNo, ushort pageLength, ushort itemCount, ulong itemTotal)
{
PageNo = pageNo;
PageLength = pageLength;
ItemCount = itemCount;
ItemTotal = itemTotal;
}
public bool Paged { get { return (PageNo > 0 && PageLength > 0 && ItemTotal > PageLength); } }
public uint PageNo { get; } = 0;
public ushort PageLength { get; } = 0;
public uint PageCount { get { return (PageLength > 0 ? (uint)Math.Ceiling((decimal)ItemTotal / PageLength) : 0); } }
public ushort ItemCount { get; } = 0;
public ulong ItemTotal { get; } = 0;
}
public PagingData Paging { get; private set; }
public void SetPaging(SbmModel_Base model)
{
Paging = new PagingData(model);
}
//---------------------------------------------------------------
}
//-------------------------------------------------------------------
}
//=======================================================================
@@ -37,13 +37,13 @@ namespace Sarand.BusinessMgr.Net.Core.Web.Helpers
/// Identify the session context that corresponds with the BusinessMgr
/// database for the specified user by email address.
/// </summary>
public static List<ushort> GetSessionContextIdxsForUsersBusMgrDatabase(String emailAddress)
public static List<ushort> GetSessionContextIdxsForContextUserEmail(String dbIdentifier, String emailAddress)
{
String fn = className + ".GetSessionContextIdxForUsersBusMgrDatabase";
String alias_SecurityItem = "SecItm";
String alias_Affiliate = "Affil";
List<ushort> sessionContextIdxs = new List<ushort>();
if (StringUtil.IsEmpty(emailAddress)) return sessionContextIdxs;
if (SacsSystem.DebugMode) SaLogger.Info($"{SysUtil.Fn} Searching for database. Identifier: {dbIdentifier}. Email address: {emailAddress}");
// Get the session context indices defined by the application.
SbmApplication_Web app = SbmGlobals_Web.Application;
@@ -53,16 +53,20 @@ namespace Sarand.BusinessMgr.Net.Core.Web.Helpers
SbmSessionContext sessionContext = app.GetSessionContext(sessionContextIdx);
if (sessionContext == null)
{
SaLogger.Warn(fn + " Failed to retrieve SessionContext for idx: " + sessionContextIdx);
SaLogger.Warn($"{SysUtil.Fn} Failed to retrieve SessionContext for idx: " + sessionContextIdx);
continue;
}
// Retrieve the BusinessMgr database.
ISacsDatabase db = app.GetBusMgrDatabase(sessionContext);
// Verify Db Identifier.
List<string> dbIdentifiers = app.GetDbIdentifiers(sessionContext);
if (!dbIdentifiers.ContainsCI(dbIdentifier))
continue;
ISacsDatabase db = app.GetBusMgrDatabase(sessionContext);
if (db == null)
{
SaLogger.Warn(fn + " Failed to retrieve SessionContext for idx: " + sessionContextIdx);
SaLogger.Warn($"{SysUtil.Fn} Failed to retrieve Database for idx: " + sessionContextIdx);
continue;
}
if (SacsSystem.DebugMode) SaLogger.Info($"{SysUtil.Fn} Database: " + db.DatabaseName);
// Query to check for Security Item matching the email with associated Affiliate.
// NB This is done independently for each database in case there are different
@@ -70,6 +74,7 @@ namespace Sarand.BusinessMgr.Net.Core.Web.Helpers
DbQuery qry = new DbQuery(db.DbmsType);
qry.Tables.Add(SaSecurityDbDfn.GetTableName(SaSecurityDbDfn.TableIdx.SaSecItem), alias_SecurityItem);
qry.Columns.Add(SaSecurityDbDfn.GetColumnNameQual(SaSecurityDbDfn.ColumnIdx.SaSecItem_Id, alias_SecurityItem));
qry.Columns.Add(SaSecurityDbDfn.GetColumnNameQual(SaSecurityDbDfn.ColumnIdx.SaSecItem_Id, alias_SecurityItem));
qry.Filters.Add(SaSecurityDbDfn.GetColumnNameQual(SaSecurityDbDfn.ColumnIdx.SaSecItem_EmailAddress, alias_SecurityItem) + "='" + DbQuery.FormatForCondition(emailAddress) + "'");
// Join to the Affiliate table.
DbQueryJoin joinAffiliate = new DbQueryJoin(DbJoinType.FullOuter);
@@ -80,7 +85,10 @@ namespace Sarand.BusinessMgr.Net.Core.Web.Helpers
using (DbResultSet resultSet = DbResultSet_DataReader.ExecuteQuery(db, qry.BuildQuery()))
{
if (resultSet != null && resultSet.NextRecord())
{
if (SacsSystem.DebugMode) SaLogger.Info($"{SysUtil.Fn} Database contains email: " + db.DatabaseName);
sessionContextIdxs.Add(sessionContextIdx);
}
}
}
return sessionContextIdxs;
@@ -36,6 +36,7 @@ namespace Sarand.BusinessMgr.Net.Core.Web.Middleware
protected override async Task<AuthenticateResult> HandleAuthenticateAsync()
{
Context.Response.StatusCode = (int)HttpStatusCode.Unauthorized;
await Task.CompletedTask;
return AuthenticateResult.NoResult();
}
@@ -6,6 +6,7 @@ using Sarand.Net.Standard.Base.Utils;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection.PortableExecutable;
using System.Text;
using System.Text.RegularExpressions;
@@ -56,10 +57,12 @@ namespace Sarand.BusinessMgr.Net.Core.Web.Middleware
string fn = GetType().Name + ".SetProperty";
//if (SbmSystem.DebugMode) Logger.Debug(fn + " Form property " + modelName + ": '" + result.Model.ToString() + ".");
object originalValue = result.Model;
// Exclude properties that retain leading/trailing whitespace.
if (result.Model is string)
// Anything that looks like a password will be left as is.
bool password = modelName.ContainsCI("Password");
// Exclude properties that retain leading/trailing whitespace.
if (result.Model is string)
{
if (!modelName.ContainsCI("Password"))
if (!password)
{
string value = result.Model as string;
@@ -89,9 +92,16 @@ namespace Sarand.BusinessMgr.Net.Core.Web.Middleware
result = ModelBindingResult.Success(value);
}
}
if (result.Model != null && SbmSystem.DebugMode)
SaLogger.Debug(fn + " Form property " + modelName + ": '" + result.Model.ToString() + "'." + (originalValue != null && !originalValue.Equals(result.Model) ? " Original value: '" + originalValue.ToString() + "'" : ""));
base.SetProperty(bindingContext, modelName, propertyMetadata, result);
// Log property details.
if (result.Model != null && SbmSystem.DebugMode &&
// Ignore passwords.
!password &&
// Ignore items that correspond with objects in the model rather than values.
!result.Model.ToString().Equals(result.Model.GetType().FullName) &&
// Ignore form files.
!result.Model.ToString().ContainsCI("Microsoft.AspNetCore.Http.IFormFile"))
SaLogger.Debug(SysUtil.Fn + " Form property " + modelName + ": '" + result.Model.ToString() + "'." + (originalValue != null && !originalValue.Equals(result.Model) ? " Original value: '" + originalValue.ToString() + "'" : ""));
base.SetProperty(bindingContext, modelName, propertyMetadata, result);
}
//-------------------------------------------------------------------
@@ -60,7 +60,7 @@ namespace Sarand.BusinessMgr.Net.Core.Web.Middleware
catch (Exception exc)
{
SaLogger.Warn(fn + " " + ExceptionUtil.GetLogMessage(exc));
throw exc;
throw;
}
}
}
+38 -103
View File
@@ -1,6 +1,8 @@
using Microsoft.AspNetCore.Html;
using Microsoft.AspNetCore.Mvc.Rendering;
using Sacs.Web.Components;
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.Mgr;
@@ -18,19 +20,17 @@ using Sarand.Net.Standard.Base.UpdateLog;
using Sarand.Net.Standard.Base.Utils;
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Text;
namespace Sarand.BusinessMgr.Net.Core.Web.Models
{
//=======================================================================
public class SbmModel_Base
public class SbmModel_Base : SaModel
{
//-------------------------------------------------------------------
// Data members.
private SbmInternals internals;
private readonly List<String> infoMessages = new List<string>();
private readonly List<String> warningMessages = new List<string>();
private readonly List<String> errorMessages = new List<string>();
//-------------------------------------------------------------------
// Protected construction.
@@ -49,16 +49,14 @@ namespace Sarand.BusinessMgr.Net.Core.Web.Models
public SbmSessionData_Web SessionData_Web { get; set; }
public SbmSessionContext SessionContext { get { return SessionData?.SessionContext ?? null; } }
public DbUpdateInfo DbUpdateInfo { get { return SessionData?.DbUpdateInfo ?? new DbUpdateInfo(null, "", SbmSystem.ComputerName); } }
public bool DebugMode { get { return SbmSystem.DebugMode; } }
public SbmLocaliser Localiser { get; }
// Request properties.
public SbmController_Base Controller { get; set; }
public CultureInfo SessionCulture { get { return Controller?.SessionCulture ?? CultureInfo.InvariantCulture; } }
//-------------------------------------------------------------------
// Page configuration.
public bool OK { get { return !HasMessages(); } }
public virtual bool ProhibitCache { get; set; } = true;
public String PageCaption { get; set; }
public String InstanceVersion { get { return !StringUtil.IsEmpty(App.AppConfig.Instance) ? App.AppConfig.Instance + " v" + Sbm.Version.GetFullVersion() : "v" + Sbm.Version.GetAppVersion(); } }
public bool IncludeGoogleAnalytics { get; set; } = false;
//-------------------------------------------------------------------
// Business components.
@@ -100,7 +98,23 @@ namespace Sarand.BusinessMgr.Net.Core.Web.Models
//-------------------------------------------------------------------
/// <summary>
/// Form control value max length.
/// Form control value max length - Security tables.
/// <para>In many cases the database columns allow plenty of extra space for the value, in case they need to be encrypted.</para>
/// <para>Hence, several cases where the value entered in the control isn't allowed the full database length.</para>
/// </summary>
public int GetControlMaxLength(SaSecurityDbDfn.ColumnIdx column)
{
int maxLength = (int)SaSecurityDbDfn.GetColTextLen(column);
switch (column)
{
case SaSecurityDbDfn.ColumnIdx.SaSecItem_EmailAddress:
return Math.Min(maxLength, 100);
}
return maxLength;
}
/// <summary>
/// Form control value max length - BusinessMgr tables.
/// <para>In many cases the database columns allow plenty of extra space for the value, in case they need to be encrypted.</para>
/// <para>Hence, several cases where the value entered in the control isn't allowed the full database length.</para>
/// </summary>
@@ -110,7 +124,7 @@ namespace Sarand.BusinessMgr.Net.Core.Web.Models
switch (column)
{
case SbmDbDfn.ColumnIdx.Affiliate_Email:
return Math.Min(maxLength, 60);
return Math.Min(maxLength, 100);
case SbmDbDfn.ColumnIdx.Affiliate_FirstName:
case SbmDbDfn.ColumnIdx.Affiliate_LastName:
return Math.Min(maxLength, 40);
@@ -138,6 +152,17 @@ namespace Sarand.BusinessMgr.Net.Core.Web.Models
}
}
//-------------------------------------------------------------------
// Display data - Dates.
public String FormatDateForDisplay(DateTime value)
{
return DateUtil.Format(value, "d-MMM-yy", SessionCulture);
}
public String FormatDateTimeForDisplay(DateTime value)
{
return StringUtil.TrimAll(DateUtil.Format(value, "d-MMM-yy HH:mm", SessionCulture));
}
//-------------------------------------------------------------------
// Form data - Dates.
// Format may depend on locale, but as long as these methods are used consistently,
@@ -208,71 +233,10 @@ namespace Sarand.BusinessMgr.Net.Core.Web.Models
/// <summary>List of Commodity Types for dropdown.</summary>
public IEnumerable<SelectListItem> GetCommodityTypeCodesForSelect()
{
return new SelectList(GetCodesOfType(Sbm.SystemCode.CommodityType), "Code", "Desc");
}
//-------------------------------------------------------------------
// Version parameter for includes.
public String HrefVersion
{
get
{
DateTime versionDate = SbmSystem.AssemblyBuildAt;
if (!DateUtil.IsValid(versionDate) || SbmSystem.DebugMode)
versionDate = DateTime.Now;
return DateUtil.Format(versionDate, "yyyyMMdd" /*+ (SacsSystem.Debug ? "HHmm" : "")*/);
}
}
// Additional includes.
public List<String> AdditionalFiles_CSS { get; set; } = new List<string>();
public List<String> AdditionalFiles_JS { get; set; } = new List<string>();
//-------------------------------------------------------------------
// Redirection locations.
public class RedirectLocationData
{
// Properties populated in Controller_Base.OnActionExecuted.
public String Base { get; internal set; }
public String Home { get; internal set; }
public String Home_SessionExpired { get; internal set; }
}
public RedirectLocationData RedirectLocation { get; } = new RedirectLocationData();
//-------------------------------------------------------------------
// Paging properties.
public class DataPaging
{
private DbQuery.Paging paging { get; } = new DbQuery.Paging();
public void PopulatePagingTo(DbQuery.Paging resultsPaging)
{
resultsPaging.PageLength = paging.PageLength;
resultsPaging.PageNo = paging.PageNo;
}
public bool IsPaged { get { return paging.Paged; } }
public uint PageNo { get { return paging.PageNo; } set { paging.PageNo = value; } }
public ushort PageLength { get { return paging.PageLength; } set { paging.PageLength = value; } }
public uint PageCount { get { return (PageLength > 0 ? (uint)Math.Ceiling((decimal)ItemTotal / PageLength) : 0); } }
public ushort ItemCount { get; set; }
private ulong itemTotal = 0;
public ulong ItemTotal
{
get { return itemTotal; }
set
{
itemTotal = value;
// Check whether the total number of items is less than the current page and reduce page no if needed.
if (IsPaged && (PageNo - 1) * PageLength > itemTotal)
PageNo = (uint)((itemTotal - 1) / PageLength + 1);
}
}
}
public DataPaging Paging { get; } = new DataPaging();
public void PopulatePagingTo(DbQuery.Paging resultsPaging)
{
Paging.PopulatePagingTo(resultsPaging);
}
//-------------------------------------------------------------------
// Form properties.
/// <summary>
@@ -280,38 +244,9 @@ namespace Sarand.BusinessMgr.Net.Core.Web.Models
/// <para>Equivalent to the Site index on the login form.</para>
/// <para>Populates value on logout form to identify default site for next login.</para>
/// </summary>
public ushort SessionIdx { get { return SessionContext.Index; } }
public ushort SessionIdx { get { return SessionContext?.Index ?? 0; } }
public String FormAction { get; set; }
//-------------------------------------------------------------------
// Messages.
//-------------------------------------------------------------------
public bool HasMessages()
{
return infoMessages.Count > 0 || warningMessages.Count > 0 || errorMessages.Count > 0;
}
public List<String> InfoMessages { get { return new List<string>(infoMessages); } }
public List<String> WarningMessages { get { return new List<string>(warningMessages); } }
public List<String> ErrorMessages { get { return new List<string>(errorMessages); } }
//-------------------------------------------------------------------
// Form messages.
public void AddInfoMessage(String message)
{
if (StringUtil.IsEmpty(StringUtil.TrimAll(message))) return;
infoMessages.Add(message);
}
public void AddWarningMessage(String message)
{
if (StringUtil.IsEmpty(StringUtil.TrimAll(message))) return;
warningMessages.Add(message);
}
public void AddErrorMessage(String message)
{
if (StringUtil.IsEmpty(StringUtil.TrimAll(message))) return;
errorMessages.Add(message);
}
//-------------------------------------------------------------------
}
//=======================================================================
+9
View File
@@ -1,5 +1,6 @@
using Sarand.Net.Standard.Base.App;
using Sarand.Net.Standard.Base.Core;
using Sarand.Net.Standard.Base.Dbas;
using System;
using System.Collections.Generic;
@@ -41,6 +42,13 @@ namespace Sarand.BusinessMgr.Net.Standard.Base.App
}
}
}
public class Database
{
public class Identifier
{
public static int MaxLength { get; } = 16;
}
}
public class Portal
{
public static class Session
@@ -82,6 +90,7 @@ namespace Sarand.BusinessMgr.Net.Standard.Base.App
//-------------------------------------------------------------------
// Definitions.
public static String ApplicationCaption { get; } = "Sarand BusinessMgr";
public static DbUpdateInfo DbUpdateInfo_Anon { get; } = new DbUpdateInfo(null, "", "");
// Option values.
public static String ValueYes { get; } = "YES";
public static String ValueNo { get; } = "NO";
@@ -12,6 +12,7 @@ using Sarand.Net.Standard.Base.ReferenceData;
using Sarand.Net.Standard.Base.Security;
using Sarand.Net.Standard.Base.UpdateLog;
using System;
using System.Collections.Generic;
namespace Sarand.BusinessMgr.Net.Standard.Base.App
{
@@ -47,14 +48,12 @@ namespace Sarand.BusinessMgr.Net.Standard.Base.App
//-------------------------------------------------------------------
// Properties.
public abstract SbmSystem System { get; }
// Control Database.
public abstract ISacsDatabase GetControlDatabase(SbmSessionContext sessionContext);
public IDatabaseInfo CreateControlDbInfo(SbmSessionContext sessionContext)
{
return GetControlDatabase(sessionContext).CreateDbInfo();
}
// Control & Template Databases.
public abstract ISacsDatabase GetControlDatabase();
public abstract ISacsDatabase GetTemplateDatabase();
// Business Mgr Database.
public abstract ISacsDatabase GetBusMgrDatabase(SbmSessionContext sessionContext);
public abstract List<string> GetDbIdentifiers(SbmSessionContext sessionContext);
public abstract ISacsDatabase GetBusMgrDatabase(SbmSessionContext sessionContext);
public IDatabaseInfo CreateBusMgrDbInfo(SbmSessionContext sessionContext)
{
return GetBusMgrDatabase(sessionContext).CreateDbInfo();
@@ -115,6 +115,7 @@ namespace Sarand.BusinessMgr.Net.Standard.Base.App
return new List<TypeCodeDesc>(systemCodeCache.GetValueOrDefault(systemCode, new List<TypeCodeDesc>()));
}
public List<TypeCodeDesc> AffiliateTypeCodes { get { return GetCodesOfType(Sbm.SystemCode.AffiliateType); } }
public List<TypeCodeDesc> AssociationTypeCodes { get { return GetCodesOfType(Sbm.SystemCode.AssociationType); } }
public List<TypeCodeDesc> CommodityTypeCodes { get { return GetCodesOfType(Sbm.SystemCode.CommodityType); } }
//-------------------------------------------------------------------
@@ -44,20 +44,20 @@ namespace Sarand.BusinessMgr.Net.Standard.Base.App
switch (textResource)
{
case TextResource.SaBaseCss:
return SacsResourceHelper.GetTextResource(SacsResourceHelper.TextResource.SaBaseCss);
return SacsResourceHelper.GetResourceText(SacsResourceHelper.TextResource.SaBaseCss);
case TextResource.SaBaseJs:
return SacsResourceHelper.GetTextResource(SacsResourceHelper.TextResource.SaBaseJs);
return SacsResourceHelper.GetResourceText(SacsResourceHelper.TextResource.SaBaseJs);
case TextResource.SbmBaseCss:
return SacsResourceHelper.GetTextResource(SacsResourceHelper.TextResource.SaBaseCss) + SacsResourceHelper.GetManifestResourceAsText(Assembly, "", "sbm_base.css");
return SacsResourceHelper.GetResourceText(SacsResourceHelper.TextResource.SaBaseCss) + SacsResourceHelper.GetManifestResourceAsText(Assembly, "", "sbm_base.css");
case TextResource.SbmBaseJs:
return SacsResourceHelper.GetTextResource(SacsResourceHelper.TextResource.SaBaseJs) + SacsResourceHelper.GetManifestResourceAsText(Assembly, "", "sbm_base.js");
return SacsResourceHelper.GetResourceText(SacsResourceHelper.TextResource.SaBaseJs) + SacsResourceHelper.GetManifestResourceAsText(Assembly, "", "sbm_base.js");
case TextResource.JQueryJs:
return SacsResourceHelper.GetTextResource(SacsResourceHelper.TextResource.JQueryJs);
return SacsResourceHelper.GetResourceText(SacsResourceHelper.TextResource.JQueryJs);
case TextResource.JQueryUiCss:
return SacsResourceHelper.GetTextResource(SacsResourceHelper.TextResource.JQueryUiCss);
return SacsResourceHelper.GetResourceText(SacsResourceHelper.TextResource.JQueryUiCss);
case TextResource.JQueryUiJs:
return SacsResourceHelper.GetTextResource(SacsResourceHelper.TextResource.JQueryUiJs);
return SacsResourceHelper.GetResourceText(SacsResourceHelper.TextResource.JQueryUiJs);
}
return "";
}
+29 -17
View File
@@ -31,27 +31,39 @@ namespace Sarand.BusinessMgr.Net.Standard.Base.App
userData = CreateUserData(SessionContext, null, null, null);
organisationData = new OrganisationData(null, null);
}
/// <summary>
/// Primary constructor - on user login.
/// <para>SessionContext and UserAccount must be supplied.</para>
/// <para>Roles list is optional.</para>
/// </summary>
protected SbmSessionData(SbmSessionContext sessionContext, SaSecurityItem securityItemUser, SbmAffiliate affiliateUser, List<SysIdString> supervisedOrganisations, SbmAssociation associationOrganisation, SbmAffiliate affiliateOrganisation)
{
SessionIdx = ++sessionIdxRoot;
ItemIdx = 1000;
SessionContext = sessionContext;
userData = CreateUserData(SessionContext, securityItemUser, affiliateUser, supervisedOrganisations);
organisationData = new OrganisationData(associationOrganisation, affiliateOrganisation);
}
/// <summary>
/// Plain constructor - Contorl application.
/// <para>SessionContext must be supplied.</para>
/// </summary>
protected SbmSessionData(SbmSessionContext sessionContext)
{
SessionIdx = ++sessionIdxRoot;
ItemIdx = 1000;
SessionContext = sessionContext;
}
/// <summary>
/// Primary constructor - on user login.
/// <para>SessionContext and UserAccount must be supplied.</para>
/// <para>Roles list is optional.</para>
/// </summary>
protected SbmSessionData(SbmSessionContext sessionContext, SaSecurityItem securityItemUser, SbmAffiliate affiliateUser, List<SysIdString> supervisedOrganisations, SbmAssociation associationOrganisation, SbmAffiliate affiliateOrganisation)
{
SessionIdx = ++sessionIdxRoot;
ItemIdx = 1000;
SessionContext = sessionContext;
userData = CreateUserData(SessionContext, securityItemUser, affiliateUser, supervisedOrganisations);
organisationData = new OrganisationData(associationOrganisation, affiliateOrganisation);
}
//-------------------------------------------------------------------
// Properties.
// Session and Item indexers.
public long SessionIdx { get; }
//-------------------------------------------------------------------
// Properties.
// Session and Item indexers.
public long SessionIdx { get; }
public int ItemIdx { get; private set; }
[JsonIgnore]
public int NextItemIdx { get { return ++ItemIdx; } }
// Session context.
public SbmSessionContext SessionContext { get; }
[JsonIgnore]
@@ -874,8 +874,9 @@ namespace Sarand.BusinessMgr.Net.Standard.Base.Bus
public Sbm.AssociationType AssociationType { get; set; } = Sbm.AssociationType.Any;
public SysId AffiliateIdOwner { get; set; }
public SysId AffiliateIdTarget { get; set; }
public SysId PriceSetId { get; set; }
public SysId AffiliateIdContactGroup { get; set; }
/// <summary>Generic search value - code, name, phone, website, email, address etc.</summary>
public String Search { get; set; }
public String Code { get; set; }
public String CodePartial { get; set; }
public String NameTargetFull { get; set; }
@@ -884,6 +885,7 @@ namespace Sarand.BusinessMgr.Net.Standard.Base.Bus
public String GroupPartial { get; set; }
public String AddressPartial { get; set; }
public String PostcodePartial { get; set; }
public SysId PriceSetId { get; set; }
// Association codes.
public String DefaultNominalCodeFull { get; set; }
public String DefaultNominalCodePartial { get; set; }
@@ -929,6 +931,14 @@ namespace Sarand.BusinessMgr.Net.Standard.Base.Bus
public DateTime ApplicableFrom { get; set; }
/// <summary>Status category.</summary>
public StatusCat StatusCat { get; set; } = StatusCat.NotDeleted;
// Aliases for tables included in the query.
public String Alias_Affiliate_Owner { get; set; }
public String Alias_Affiliate_Target { get; set; }
public String Alias_Address_Target { get; set; }
public String Alias_Association_Member { get; set; }
public String Alias_Address_Member { get; set; }
public String Alias_PriceSet { get; set; }
}
}
//=======================================================================
@@ -38,7 +38,7 @@ namespace Sarand.BusinessMgr.Net.Standard.Base.Core
IUserData User { get; }
bool ValidUser { get; }
/// <summary>Database upfate info - user session specific properties for database inserts/updates.</summary>
/// <summary>Database update info - user session specific properties for database inserts/updates.</summary>
DbUpdateInfo DbUpdateInfo { get; }
//-------------------------------------------------------------------
@@ -542,10 +542,10 @@ namespace Sarand.BusinessMgr.Net.Standard.Base.Mgr
ClearErrorState();
if (!SysId.IsValid(affiliateIdOwner) || (int)associationType < 1 || !SysId.IsValid(affiliateIdTarget)) return null;
SbmAssociation.Params prmAssociation = new SbmAssociation.Params();
prmAssociation.IncludedIds.Add(affiliateIdOwner);
prmAssociation.AffiliateIdOwner = affiliateIdOwner;
prmAssociation.AssociationType = associationType;
prmAssociation.IncludedIds.Add(affiliateIdTarget);
prmAssociation.StatusCat = SbmAssociation.StatusCat.Any;
prmAssociation.AffiliateIdTarget = affiliateIdTarget;
prmAssociation.StatusCat = SbmAssociation.StatusCat.NotDeleted;
return GetAssociation(prmAssociation);
}
public SbmAssociation GetAssociationByTypeCode(Sbm.AssociationType associationType, String associationCode)
@@ -556,8 +556,8 @@ namespace Sarand.BusinessMgr.Net.Standard.Base.Mgr
{
AssociationType = associationType,
Code = associationCode,
StatusCat = SbmAssociation.StatusCat.Any
};
StatusCat = SbmAssociation.StatusCat.NotDeleted
};
return GetAssociation(prmAssociation);
}
@@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netstandard2.1</TargetFramework>
<TargetFramework>net6.0</TargetFramework>
<RootNamespace>Sarand.BusinessMgr.Net.Standard</RootNamespace>
</PropertyGroup>
@@ -1318,6 +1318,7 @@ namespace Sarand.BusinessMgr.Net.Standard.Dbas.Mgr
{
List<String> filter = new List<String>();
//---------------------------------------------------------------
// Association Id.
String colAssociation_Id = SbmDbDfn.GetColumnNameQual(SbmDbDfn.ColumnIdx.Association_Id, aliasAssociation);
String includedIds = prm.IncludedIds.Concat(",");
@@ -1332,6 +1333,7 @@ namespace Sarand.BusinessMgr.Net.Standard.Dbas.Mgr
if (prm.AssociationType > 0)
filter.Add(colAssociation_Type + "=" + ((int)prm.AssociationType).ToString());
//---------------------------------------------------------------
// Owner affiliate.
String colAssociation_AffIdOwner = SbmDbDfn.GetColumnNameQual(SbmDbDfn.ColumnIdx.Association_AffIdOwner, aliasAssociation);
if (SysId.IsValid(prm.AffiliateIdOwner))
@@ -1342,13 +1344,8 @@ namespace Sarand.BusinessMgr.Net.Standard.Dbas.Mgr
if (SysId.IsValid(prm.AffiliateIdTarget))
filter.Add(colAssociation_AffIdTarget + "=" + prm.AffiliateIdTarget.ToString());
// Price Set.
String colAssociation_PriceSetId = SbmDbDfn.GetColumnNameQual(SbmDbDfn.ColumnIdx.Association_PriceSetId, aliasAssociation);
if (SysId.IsValid(prm.PriceSetId))
filter.Add(colAssociation_PriceSetId + "=" + prm.PriceSetId.ToString());
//---------------------------------------------------------------
// Contact Group:
// Contact Group.
if (SysId.IsValid(prm.AffiliateIdContactGroup))
{
//Association must also be a member of this contact group.
@@ -1368,54 +1365,80 @@ namespace Sarand.BusinessMgr.Net.Standard.Dbas.Mgr
}
//---------------------------------------------------------------
// Affiliate TargetName & Partial TargetName attributes.
// Code.
String colAssociation_Code = SbmDbDfn.GetColumnNameQual(SbmDbDfn.ColumnIdx.Association_Code, aliasAssociation);
if (!prm.Code.IsEmpty())
filter.Add(colAssociation_Code + "='" + DbQuery.FormatForCondition(prm.Code) + "'");
else if (!prm.CodePartial.IsEmpty())
filter.Add(DbQuery.FormatFn_Left(colAssociation_Code, prm.CodePartial.Length) + " Like '%" + DbQuery.FormatForLike(prm.CodePartial) + "%'");
// Group.
String colAssociation_Group = SbmDbDfn.GetColumnNameQual(SbmDbDfn.ColumnIdx.Association_Group, aliasAssociation);
if (!prm.GroupFull.IsEmpty())
filter.Add(colAssociation_Group + "='" + DbQuery.FormatForCondition(prm.GroupFull) + "'");
else if (!prm.GroupPartial.IsEmpty())
filter.Add(DbQuery.FormatFn_Left(colAssociation_Group, prm.GroupPartial.Length) + " Like '%" + DbQuery.FormatForLike(prm.GroupPartial) + "%'");
//---------------------------------------------------------------
// Target Name.
if (!prm.NameTargetFull.IsEmpty() || !prm.NameTargetPartial.IsEmpty())
{
// Target name has been specified.
String tableAffiliate = SbmDbDfn.GetTableName(SbmDbDfn.TableIdx.Affiliate);
String colAffiliate_Id = SbmDbDfn.GetColumnNameQual(SbmDbDfn.ColumnIdx.Affiliate_Id);
String colAffiliate_Name = SbmDbDfn.GetColumnNameQual(SbmDbDfn.ColumnIdx.Affiliate_Name);
String colAffiliate_FirstName = SbmDbDfn.GetColumnNameQual(SbmDbDfn.ColumnIdx.Affiliate_FirstName);
String colAfiliate_LastName = SbmDbDfn.GetColumnNameQual(SbmDbDfn.ColumnIdx.Affiliate_LastName);
// Target affiliate alias.
String aliasAffiliateTarget = StringUtil.OnEmpty(prm.Alias_Affiliate_Target, aliasAssociation_Affiliate_Target);
// Clause to identify associations with targets that match the partial name.
DbQuery queryAffTarget = new DbQuery(db.DbmsType);
queryAffTarget.Tables.Add(tableAffiliate);
queryAffTarget.Columns.Add(colAffiliate_Id);
queryAffTarget.Filters.Add(colAffiliate_Id + "=" + colAssociation_AffIdTarget);
// Target affiliate columns.
String colAffiliate_Id = SbmDbDfn.GetColumnNameQual(SbmDbDfn.ColumnIdx.Affiliate_Id, aliasAffiliateTarget);
String colAffiliate_Name = SbmDbDfn.GetColumnNameQual(SbmDbDfn.ColumnIdx.Affiliate_Name, aliasAffiliateTarget);
String colAffiliate_FirstName = SbmDbDfn.GetColumnNameQual(SbmDbDfn.ColumnIdx.Affiliate_FirstName, aliasAffiliateTarget);
String colAffiliate_LastName = SbmDbDfn.GetColumnNameQual(SbmDbDfn.ColumnIdx.Affiliate_LastName, aliasAffiliateTarget);
// Filters.
List<String> affiliateFilter = new List<string>();
String nameTargetFull = DbQuery.FormatForCondition(StringUtil.EmptyOnNull(prm.NameTargetFull));
String nameTargetPartial = DbQuery.FormatForCondition(StringUtil.EmptyOnNull(prm.NameTargetPartial));
String nameTargetPartial = DbQuery.FormatForLike(StringUtil.EmptyOnNull(prm.NameTargetPartial));
if (!nameTargetFull.IsEmpty())
queryAffTarget.Filters.Add(colAffiliate_Name + "='" + nameTargetFull + "' OR " + colAfiliate_LastName + "='" + nameTargetFull + "'");
affiliateFilter.Add(colAffiliate_Name + "='" + nameTargetFull + "' OR " + colAffiliate_LastName + "='" + nameTargetFull + "'");
if (!nameTargetPartial.IsEmpty())
queryAffTarget.Filters.Add(
DbQuery.FormatFn_Left(colAffiliate_Name, nameTargetPartial.Length) + "='" + nameTargetPartial + "' OR " +
DbQuery.FormatFn_Left(colAffiliate_FirstName, nameTargetPartial.Length) + "='" + nameTargetPartial + "' OR " +
DbQuery.FormatFn_Left(colAfiliate_LastName, nameTargetPartial.Length) + "='" + nameTargetPartial + "'");
filter.Add("EXISTS (" + queryAffTarget.BuildQuery() + ")");
affiliateFilter.Add(
colAffiliate_Name + " Like '%" + nameTargetPartial + "%' OR " +
colAffiliate_FirstName + " Like '%" + nameTargetPartial + "%' OR " +
colAffiliate_LastName + " Like '%" + nameTargetPartial + "%'");
if (prm.Alias_Affiliate_Target.IsEmpty())
{
// Target affiliate alias not supplied - table is not joined in main query.
// Sub query on target affiliate.
DbQuery queryAffTarget = new DbQuery(db.DbmsType);
queryAffTarget.Tables.Add(SbmDbDfn.GetTableName(SbmDbDfn.TableIdx.Affiliate), aliasAffiliateTarget);
queryAffTarget.Columns.Add(colAffiliate_Id);
queryAffTarget.Filters.Add(colAffiliate_Id + "=" + colAssociation_AffIdTarget);
queryAffTarget.Filters.AddRange(affiliateFilter);
filter.Add("EXISTS (" + queryAffTarget.BuildQuery() + ")");
}
else
{
// Target affiliate alias supplied - table is joined in main query.
// Simply filter directly on joined table.
filter.AddRange(affiliateFilter);
}
}
//---------------------------------------------------------------
// Partial Address & Postcode for AddTarget.
// Target Address & Postcode.
if (!StringUtil.IsEmpty(prm.AddressPartial) || !StringUtil.IsEmpty(prm.PostcodePartial))
{
// Clause to identify associations with target addresses that match the details specified.
String aliasAddTarget = "aAddTarget";
DbQuery queryAddTarget = new DbQuery(db.DbmsType);
String tableAddress = SbmDbDfn.GetTableName(SbmDbDfn.TableIdx.Address);
queryAddTarget.Tables.Add(tableAddress, aliasAddTarget);
// Target Address alias.
String aliasAddTarget = StringUtil.OnEmpty(prm.Alias_Address_Target, "aAddTarget");
// Target address columns.
String colAddress_Id = SbmDbDfn.GetColumnNameQual(SbmDbDfn.ColumnIdx.Address_Id, aliasAddTarget);
queryAddTarget.Columns.Add(colAddress_Id);
String colAddress_ItemType = SbmDbDfn.GetColumnNameQual(SbmDbDfn.ColumnIdx.Address_ItemType, aliasAddTarget);
queryAddTarget.Filters.Add(colAddress_ItemType + "=" + ((int)Sbm.ItemType.Affiliate).ToString());
String colAddress_ItemId = SbmDbDfn.GetColumnNameQual(SbmDbDfn.ColumnIdx.Address_ItemId, aliasAddTarget);
queryAddTarget.Filters.Add(colAddress_ItemId + "=" + colAssociation_AffIdTarget);
// Address filter.
String addressLike = DbQuery.FormatForLike(StringUtil.EmptyOnNull(prm.AddressPartial));
if (!StringUtil.IsEmpty(addressLike))
// Filters.
List<String> addressFilter = new List<string>();
String addressPartial = DbQuery.FormatForLike(StringUtil.EmptyOnNull(prm.AddressPartial));
if (!StringUtil.IsEmpty(addressPartial))
{
List<SbmDbDfn.ColumnIdx> addressCols = new List<SbmDbDfn.ColumnIdx>()
{
@@ -1427,45 +1450,133 @@ namespace Sarand.BusinessMgr.Net.Standard.Dbas.Mgr
SbmDbDfn.ColumnIdx.Address_Province,
SbmDbDfn.ColumnIdx.Address_Contact,
};
List<String> filterAddress = new List<String>();
foreach (SbmDbDfn.ColumnIdx addressCol in addressCols)
filterAddress.Add(SbmDbDfn.GetColumnNameQual(addressCol, aliasAddTarget) + " LIKE '%" + addressLike + "%'");
queryAddTarget.Filters.Add(filterAddress.Concat(" OR ", "(", ")"));
String partialAddressFilter = addressCols.Select(item => SbmDbDfn.GetColumnNameQual(item, aliasAddTarget) + " LIKE '%" + addressPartial + "%'").Concat(" OR ", "(", ")");
addressFilter.Add(partialAddressFilter);
}
// Postcode filter.
String postcodeCond = DbQuery.FormatForCondition(StringUtil.EmptyOnNull(prm.PostcodePartial));
String colAddress_PostcodeFmt = SbmDbDfn.GetColumnNameQual(SbmDbDfn.ColumnIdx.Address_PostcodeFmt, aliasAddTarget);
if (!StringUtil.IsEmpty(postcodeCond))
queryAddTarget.Filters.Add(DbQuery.FormatFn_Left(colAddress_PostcodeFmt, prm.PostcodePartial.Length) + "='" + postcodeCond + "'");
filter.Add("EXISTS (" + queryAddTarget.BuildQuery() + ")");
addressFilter.Add(DbQuery.FormatFn_Left(colAddress_PostcodeFmt, prm.PostcodePartial.Length) + "='" + postcodeCond + "'");
if (prm.Alias_Address_Target.IsEmpty())
{
// Target address alias not supplied - table is not joined in main query.
// Sub query on target address.
DbQuery queryAddTarget = new DbQuery(db.DbmsType);
queryAddTarget.Tables.Add(SbmDbDfn.GetTableName(SbmDbDfn.TableIdx.Address), aliasAddTarget);
queryAddTarget.Columns.Add(colAddress_Id);
queryAddTarget.Filters.Add(colAddress_ItemType + "=" + ((int)Sbm.ItemType.Affiliate).ToString());
queryAddTarget.Filters.Add(colAddress_ItemId + "=" + colAssociation_AffIdTarget);
queryAddTarget.Filters.AddRange(addressFilter);
filter.Add("EXISTS (" + queryAddTarget.BuildQuery() + ")");
}
else
{
// Target address alias supplied - table is joined in main query.
// Simply filter directly on joined table.
filter.AddRange(addressFilter);
}
}
// Full & Partial DefaultNominalCode.
//---------------------------------------------------------------
// Generic search.
if (!StringUtil.IsEmpty(prm.Search))
{
List<String> searchFilter = new List<string>();
foreach (String rawSearchValue in prm.Search.Split(new char[] { ' ', ',', ';' }))
{
String searchValue = DbQuery.FormatForLike(StringUtil.TrimAll(rawSearchValue));
if (StringUtil.IsEmpty(searchValue)) continue;
// Association filters.
List<String> searchValueFilter = new List<string>();
searchValueFilter.Add(colAssociation_Code + " LIKE '%" + searchValue + "%'");
searchValueFilter.Add(colAssociation_Group + " LIKE '%" + searchValue + "%'");
// Target affiliate.
String aliasAffiliateTarget = StringUtil.OnEmpty(prm.Alias_Affiliate_Target, aliasAssociation_Affiliate_Target);
String colAffiliate_Id = SbmDbDfn.GetColumnNameQual(SbmDbDfn.ColumnIdx.Affiliate_Id, aliasAffiliateTarget);
String colAffiliate_Name = SbmDbDfn.GetColumnNameQual(SbmDbDfn.ColumnIdx.Affiliate_Name, aliasAffiliateTarget);
List<String> affiliateFilter = new List<string>();
affiliateFilter.Add(colAffiliate_Name + " Like '%" + searchValue + "%'");
if (prm.Alias_Affiliate_Target.IsEmpty())
{
// Target affiliate alias not supplied - table is not joined in main query.
// Sub query on target affiliate.
DbQuery queryAffTarget = new DbQuery(db.DbmsType);
queryAffTarget.Tables.Add(SbmDbDfn.GetTableName(SbmDbDfn.TableIdx.Affiliate), aliasAffiliateTarget);
queryAffTarget.Columns.Add(colAffiliate_Id);
queryAffTarget.Filters.Add(colAffiliate_Id + "=" + colAssociation_AffIdTarget);
queryAffTarget.Filters.AddRange(affiliateFilter);
filter.Add("EXISTS (" + queryAffTarget.BuildQuery() + ")");
}
else
{
// Target affiliate alias supplied - table is joined in main query.
// Simply filter directly on joined table.
searchValueFilter.AddRange(affiliateFilter);
}
// Target address.
String aliasAddTarget = StringUtil.OnEmpty(prm.Alias_Address_Target, "aAddTarget");
String colAddress_Id = SbmDbDfn.GetColumnNameQual(SbmDbDfn.ColumnIdx.Address_Id, aliasAddTarget);
String colAddress_ItemType = SbmDbDfn.GetColumnNameQual(SbmDbDfn.ColumnIdx.Address_ItemType, aliasAddTarget);
String colAddress_ItemId = SbmDbDfn.GetColumnNameQual(SbmDbDfn.ColumnIdx.Address_ItemId, aliasAddTarget);
List<String> addressFilter = new List<string>();
List<SbmDbDfn.ColumnIdx> addressCols = new List<SbmDbDfn.ColumnIdx>()
{
SbmDbDfn.ColumnIdx.Address_Name,
SbmDbDfn.ColumnIdx.Address_Line1,
SbmDbDfn.ColumnIdx.Address_Line2,
SbmDbDfn.ColumnIdx.Address_Line3,
SbmDbDfn.ColumnIdx.Address_City,
SbmDbDfn.ColumnIdx.Address_Province,
SbmDbDfn.ColumnIdx.Address_Postcode,
SbmDbDfn.ColumnIdx.Address_PostcodeFmt,
SbmDbDfn.ColumnIdx.Address_Contact,
};
String partialAddressFilter = addressCols.Select(item => SbmDbDfn.GetColumnNameQual(item, aliasAddTarget) + " LIKE '%" + searchValue + "%'").Concat(" OR ", "(", ")");
addressFilter.Add(partialAddressFilter);
if (prm.Alias_Address_Target.IsEmpty())
{
// Target address alias not supplied - table is not joined in main query.
// Sub query on target address.
DbQuery queryAddTarget = new DbQuery(db.DbmsType);
queryAddTarget.Tables.Add(SbmDbDfn.GetTableName(SbmDbDfn.TableIdx.Address), aliasAddTarget);
queryAddTarget.Columns.Add(colAddress_Id);
queryAddTarget.Filters.Add(colAddress_ItemType + "=" + ((int)Sbm.ItemType.Affiliate).ToString());
queryAddTarget.Filters.Add(colAddress_ItemId + "=" + colAssociation_AffIdTarget);
queryAddTarget.Filters.AddRange(addressFilter);
filter.Add("EXISTS (" + queryAddTarget.BuildQuery() + ")");
}
else
{
// Target address alias supplied - table is joined in main query.
// Simply filter directly on joined table.
searchValueFilter.AddRange(addressFilter);
}
searchFilter.Add(searchValueFilter.Concat(" OR ", "(", ")"));
}
filter.AddRange(searchFilter);
}
//---------------------------------------------------------------
// Default Nominal Code.
String defaultNominalCodeFull = DbQuery.FormatForCondition(StringUtil.EmptyOnNull(prm.DefaultNominalCodeFull));
String defaultNominalCodePartial = DbQuery.FormatForCondition(StringUtil.EmptyOnNull(prm.DefaultNominalCodePartial));
String colAssociation_DefaultNominalCode = SbmDbDfn.GetColumnNameQual(SbmDbDfn.ColumnIdx.Association_DefaultNominalCode, aliasAssociation);
if (!defaultNominalCodeFull.IsEmpty())
filter.Add(colAssociation_DefaultNominalCode + "='" + defaultNominalCodeFull + "'");
filter.Add(colAssociation_DefaultNominalCode + "='" + DbQuery.FormatForCondition(defaultNominalCodeFull) + "'");
else if (!defaultNominalCodePartial.IsEmpty())
filter.Add(DbQuery.FormatFn_Left(colAssociation_DefaultNominalCode, prm.DefaultNominalCodePartial.Length) + "='" + defaultNominalCodePartial + "'");
filter.Add(DbQuery.FormatFn_Left(colAssociation_DefaultNominalCode, prm.DefaultNominalCodePartial.Length) + " Like '%" + DbQuery.FormatForLike(defaultNominalCodePartial) + "%'");
// Full & Partial Code.
String codeFull = DbQuery.FormatForCondition(StringUtil.EmptyOnNull(prm.Code));
String codePartial = DbQuery.FormatForCondition(StringUtil.EmptyOnNull(prm.CodePartial));
String colAssociation_Code = SbmDbDfn.GetColumnNameQual(SbmDbDfn.ColumnIdx.Association_Code, aliasAssociation);
if (!codeFull.IsEmpty())
filter.Add(colAssociation_Code + "='" + codeFull + "'");
else if (!codePartial.IsEmpty())
filter.Add(DbQuery.FormatFn_Left(colAssociation_Code, prm.CodePartial.Length) + "='" + codePartial + "'");
// Full & Partial Group.
String groupFull = DbQuery.FormatForCondition(StringUtil.EmptyOnNull(prm.GroupFull));
String groupPartial = DbQuery.FormatForCondition(StringUtil.EmptyOnNull(prm.GroupPartial));
String colAssociation_Group = SbmDbDfn.GetColumnNameQual(SbmDbDfn.ColumnIdx.Association_Group, aliasAssociation);
if (!groupFull.IsEmpty())
filter.Add(colAssociation_Group + "='" + groupFull + "'");
else if (!groupPartial.IsEmpty())
filter.Add(DbQuery.FormatFn_Left(colAssociation_Group, prm.GroupPartial.Length) + "='" + groupPartial + "'");
//---------------------------------------------------------------
// Price Set.
String colAssociation_PriceSetId = SbmDbDfn.GetColumnNameQual(SbmDbDfn.ColumnIdx.Association_PriceSetId, aliasAssociation);
if (SysId.IsValid(prm.PriceSetId))
filter.Add(colAssociation_PriceSetId + "=" + prm.PriceSetId.ToString());
//---------------------------------------------------------------
// Codings.
@@ -1943,20 +2054,20 @@ namespace Sarand.BusinessMgr.Net.Standard.Dbas.Mgr
query.Columns.Add(SbmDbDfn.GetColumnNameQual(SbmDbDfn.ColumnIdx.Association_UpdateOn, aliasAssociation));
//---------------------------------------------------------------
// Owner affiliate columns
// Owner affiliate.
bool hasColsAffOwner = false;
if (!hasColsAffOwner)
hasColsAffOwner = columns.HasId((int)SbmAssociation.Column.Owner_Name);
bool hasColsAffOwnerAddress = false;
if (hasColsAffOwner || hasColsAffOwnerAddress)
{
// Join to the owner affiliate.
DbQueryJoin joinPriceSetOwner = new DbQueryJoin(DbJoinType.LeftOuter);
joinPriceSetOwner.Table = new StringPair(SbmDbDfn.GetTableName(SbmDbDfn.TableIdx.Affiliate), aliasAssociation_Affiliate_Owner);
joinPriceSetOwner.Filters.Add(SbmDbDfn.GetColumnNameQual(SbmDbDfn.ColumnIdx.Affiliate_Id, aliasAssociation_Affiliate_Owner) + "=" + SbmDbDfn.GetColumnNameQual(SbmDbDfn.ColumnIdx.Association_AffIdOwner, aliasAssociation));
query.Joins.Add(joinPriceSetOwner);
// Add the owner affiliate columns.
prm.Alias_Affiliate_Owner = aliasAssociation_Affiliate_Owner;
DbQueryJoin joinAffiliateOwner = new DbQueryJoin(DbJoinType.LeftOuter);
joinAffiliateOwner.Table = new StringPair(SbmDbDfn.GetTableName(SbmDbDfn.TableIdx.Affiliate), aliasAssociation_Affiliate_Owner);
joinAffiliateOwner.Filters.Add(SbmDbDfn.GetColumnNameQual(SbmDbDfn.ColumnIdx.Affiliate_Id, aliasAssociation_Affiliate_Owner) + "=" + SbmDbDfn.GetColumnNameQual(SbmDbDfn.ColumnIdx.Association_AffIdOwner, aliasAssociation));
query.Joins.Add(joinAffiliateOwner);
// Columns.
List<SbmDbDfn.ColumnIdx> affiliateOwnerCols = new List<SbmDbDfn.ColumnIdx>()
{
SbmDbDfn.ColumnIdx.Affiliate_Id,
@@ -1979,7 +2090,7 @@ namespace Sarand.BusinessMgr.Net.Standard.Dbas.Mgr
}
//---------------------------------------------------------------
// Target affiliate columns.
// Target affiliate.
bool hasColsAffTarget = false;
if (!hasColsAffTarget) hasColsAffTarget = columns.HasId((int)SbmAssociation.Column.Target_Type) || columns.HasId((int)SbmAssociation.Column.Target_TypeDesc);
if (!hasColsAffTarget) hasColsAffTarget = columns.HasId((int)SbmAssociation.Column.Target_Name);
@@ -2021,10 +2132,11 @@ namespace Sarand.BusinessMgr.Net.Standard.Dbas.Mgr
if (hasColsAffTarget || hasColsAffTargetAddress)
{
// Join to the target affiliate - NB Use an alias to avoid clashing with the owner affiliate.
DbQueryJoin joinPriceSetTarget = new DbQueryJoin(DbJoinType.LeftOuter);
joinPriceSetTarget.Table = new StringPair(SbmDbDfn.GetTableName(SbmDbDfn.TableIdx.Affiliate), aliasAssociation_Affiliate_Target);
joinPriceSetTarget.Filters.Add(SbmDbDfn.GetColumnNameQual(SbmDbDfn.ColumnIdx.Affiliate_Id, aliasAssociation_Affiliate_Target) + "=" + SbmDbDfn.GetColumnNameQual(SbmDbDfn.ColumnIdx.Association_AffIdTarget, aliasAssociation));
query.Joins.Add(joinPriceSetTarget);
prm.Alias_Affiliate_Target = aliasAssociation_Affiliate_Target;
DbQueryJoin joinAffiliateTarget = new DbQueryJoin(DbJoinType.LeftOuter);
joinAffiliateTarget.Table = new StringPair(SbmDbDfn.GetTableName(SbmDbDfn.TableIdx.Affiliate), aliasAssociation_Affiliate_Target);
joinAffiliateTarget.Filters.Add(SbmDbDfn.GetColumnNameQual(SbmDbDfn.ColumnIdx.Affiliate_Id, aliasAssociation_Affiliate_Target) + "=" + SbmDbDfn.GetColumnNameQual(SbmDbDfn.ColumnIdx.Association_AffIdTarget, aliasAssociation));
query.Joins.Add(joinAffiliateTarget);
// Add the target affiliate columns.
List<SbmDbDfn.ColumnIdx> affiliateTargetCols = new List<SbmDbDfn.ColumnIdx>();
affiliateTargetCols.Add(SbmDbDfn.ColumnIdx.Affiliate_Id);
@@ -2053,6 +2165,7 @@ namespace Sarand.BusinessMgr.Net.Standard.Dbas.Mgr
if (hasColsAffTargetAddress)
{
// Join to the target contact's primary address.
prm.Alias_Address_Target = aliasAssociation_Target_Address;
DbQueryJoin joinTargetAddress = new DbQueryJoin(DbJoinType.LeftOuter);
joinTargetAddress.Table = new StringPair(SbmDbDfn.GetTableName(SbmDbDfn.TableIdx.Address), aliasAssociation_Target_Address);
// Address context.
@@ -2174,6 +2287,7 @@ namespace Sarand.BusinessMgr.Net.Standard.Dbas.Mgr
if (hasColsMember || hasColsMemberAddress)
{
// Join to the member association - NB Use an alias to avoid clashing with the target affiliate.
prm.Alias_Association_Member = aliasAssociation_Member_Association;
DbQueryJoin joinPriceSetMemberAss = new DbQueryJoin(DbJoinType.LeftOuter);
joinPriceSetMemberAss.Table = new StringPair(SbmDbDfn.GetTableName(SbmDbDfn.TableIdx.Association), aliasAssociation_Member_Association);
joinPriceSetMemberAss.Filters.Add(SbmDbDfn.GetColumnNameQual(SbmDbDfn.ColumnIdx.Association_AffIdOwner, aliasAssociation_Member_Association) + "=" + SbmDbDfn.GetColumnNameQual(SbmDbDfn.ColumnIdx.Association_AffIdTarget));
@@ -2215,6 +2329,7 @@ namespace Sarand.BusinessMgr.Net.Standard.Dbas.Mgr
if (hasColsMemberAddress)
{
// Join to the Member affiliate - NB Use an alias to avoid clashing with the owner affiliate.
prm.Alias_Address_Member = aliasAssociation_Member_Address;
DbQueryJoin joinPriceSetMemberAddress = new DbQueryJoin(DbJoinType.LeftOuter);
joinPriceSetMemberAddress.Table = new StringPair(SbmDbDfn.GetTableName(SbmDbDfn.TableIdx.Address), aliasAssociation_Member_Address);
joinPriceSetMemberAddress.Filters.Add(SbmDbDfn.GetColumnNameQual(SbmDbDfn.ColumnIdx.Address_AffIdOwner, aliasAssociation_Member_Address) + "=" + SbmDbDfn.GetColumnNameQual(SbmDbDfn.ColumnIdx.Affiliate_Id, aliasAssociation_Member_Affiliate));
@@ -2254,6 +2369,7 @@ namespace Sarand.BusinessMgr.Net.Standard.Dbas.Mgr
if (hasColsPriceSet)
{
// Join to the Price Set.
prm.Alias_PriceSet = aliasAssociation_PriceSet;
DbQueryJoin joinPriceSet = new DbQueryJoin(DbJoinType.LeftOuter);
joinPriceSet.Table = new StringPair(SbmDbDfn.GetTableName(SbmDbDfn.TableIdx.SaPriceSet), aliasAssociation_PriceSet);
joinPriceSet.Filters.Add(SbmDbDfn.GetColumnNameQual(SbmDbDfn.ColumnIdx.SaPriceSet_Id, aliasAssociation_PriceSet) + "=" + SbmDbDfn.GetColumnNameQual(SbmDbDfn.ColumnIdx.Association_PriceSetId, aliasAssociation));
@@ -57,7 +57,7 @@ namespace Sarand.BusinessMgr.Net.Standard.Dbas.Utils
OutputDirectory = directory
};
// Schema - Tables.
config.Schema.Tables_Excluded.AddRange(new String[] { });
config.Schema.Tables_Excluded.UnionWith(new String[] { });
// Schema - Columns.
config.Schema.InsertOnlyColumns.Add(".*", "CreateOn,CreateBy,CreateHost");
// Database definition class.
@@ -63,7 +63,7 @@ namespace Sarand.BusinessMgr.Net.Standard.Dbas.Utils
OutputDirectory = directory
};
// Schema - Tables.
config.Schema.Tables_Excluded.AddRange(new String[] { "NextId", "SaCode", "SaCodeType", "SaSecItem", "SaSecurityItem", "SaSecLink", "SaSecurityLink", "SaItemData", "SaItemDoc", "SaItemOption", "SaInterface", "SaInterfaceProcess", "SaInterfaceProcessMessage", "SaInterfaceDataItem", "SaInterfaceProcessDataItem", "SaAttrUpdate" });
config.Schema.Tables_Excluded.UnionWith(new String[] { "NextId", "SaCode", "SaCodeType", "SaSecItem", "SaSecurityItem", "SaSecLink", "SaSecurityLink", "SaItemData", "SaItemDoc", "SaItemOption", "SaInterface", "SaInterfaceProcess", "SaInterfaceProcessMessage", "SaInterfaceDataItem", "SaInterfaceProcessDataItem", "SaAttrUpdate" });
// Schema - Columns.
config.Schema.InsertOnlyColumns.Add(".*", "CreateOn,CreateBy,CreateHost");
// Database definition class.
@@ -1,19 +1,19 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netstandard2.1</TargetFramework>
<RootNamespace>Sarand.BusinessMgr.Net.Standard</RootNamespace>
</PropertyGroup>
<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
<RootNamespace>Sarand.BusinessMgr.Net.Standard</RootNamespace>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\..\..\Common-VS\Workspace\SacsStandardBase\SacsStandardBase.csproj" />
<ProjectReference Include="..\..\..\Common-VS\Workspace\SacsStandardDbas\SacsStandardDbas.csproj" />
<ProjectReference Include="..\SaBusMgrStandardBase\SaBusMgrStandardBase.csproj" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\..\Common-VS\Workspace\SacsStandardBase\SacsStandardBase.csproj" />
<ProjectReference Include="..\..\..\Common-VS\Workspace\SacsStandardDbas\SacsStandardDbas.csproj" />
<ProjectReference Include="..\SaBusMgrStandardBase\SaBusMgrStandardBase.csproj" />
</ItemGroup>
<ItemGroup>
<Folder Include="Dbas\Internal\Control\" />
<Folder Include="Resources\Database\" />
</ItemGroup>
<ItemGroup>
<Folder Include="Dbas\Internal\Control\" />
<Folder Include="Resources\Database\" />
</ItemGroup>
</Project>
+55 -52
View File
@@ -1,54 +1,57 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<SSDTUnitTestPath Condition="'$(SSDTUnitTestPath)' == ''">$(VsInstallRoot)\Common7\IDE\Extensions\Microsoft\SQLDB</SSDTUnitTestPath>
</PropertyGroup>
<PropertyGroup>
<SSDTPath Condition="'$(SSDTPath)' == ''">$(VsInstallRoot)\Common7\IDE\Extensions\Microsoft\SQLDB\DAC\130</SSDTPath>
</PropertyGroup>
<PropertyGroup>
<VisualStudioVersion Condition="'$(VisualStudioVersion)' == ''">10.0</VisualStudioVersion>
</PropertyGroup>
<PropertyGroup>
<TargetFramework>netcoreapp3.1</TargetFramework>
<IsPackable>false</IsPackable>
<RootNamespace>Sarand.BusinessMgr</RootNamespace>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.5.0" />
<PackageReference Include="Moq" Version="4.16.1" />
<PackageReference Include="MSTest.TestAdapter" Version="2.1.0" />
<PackageReference Include="MSTest.TestFramework" Version="2.1.0" />
<PackageReference Include="coverlet.collector" Version="1.2.0" />
</ItemGroup>
<ItemGroup Condition="$(VisualStudioVersion) == '16.0'">
<Reference Include="Microsoft.Data.Tools.Schema.Sql, Version=15.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
<HintPath>$(SSDTPath)\Microsoft.Data.Tools.Schema.Sql.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="Microsoft.Data.Tools.Schema.Sql.UnitTesting, Version=16.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
<HintPath>$(SSDTUnitTestPath)\Microsoft.Data.Tools.Schema.Sql.UnitTesting.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="Microsoft.Data.Tools.Schema.Sql.UnitTestingAdapter, Version=16.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
<HintPath>$(SSDTUnitTestPath)\Microsoft.Data.Tools.Schema.Sql.UnitTestingAdapter.dll</HintPath>
<Private>True</Private>
</Reference>
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\..\Common-VS\Workspace\SacsCoreWeb\SacsCoreWeb.csproj" />
<ProjectReference Include="..\..\..\Common-VS\Workspace\SacsStandardAccounts\SacsStandardAccounts.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="..\SaBusinessMgrCoreWebPortal\SaBusinessMgrCoreWebPortal.csproj" />
<ProjectReference Include="..\SaBusMgrCoreProcs\SaBusMgrCoreProcs.csproj" />
<ProjectReference Include="..\SaBusMgrCoreWeb\SaBusMgrCoreWeb.csproj" />
<ProjectReference Include="..\SaBusMgrStandardBase\SaBusMgrStandardBase.csproj" />
<ProjectReference Include="..\SaBusMgrStandardDbas\SaBusMgrStandardDbas.csproj" />
</ItemGroup>
<PropertyGroup>
<SsdtUnitTestVersion>3.1</SsdtUnitTestVersion>
</PropertyGroup>
<Import Project="$(SQLDBExtensionsRefPath)\Microsoft.Data.Tools.Schema.Sql.UnitTesting.targets" Condition="$(VisualStudioVersion) != '15.0' And '$(SQLDBExtensionsRefPath)' != ''" />
<Import Project="$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)\SSDT\Microsoft.Data.Tools.Schema.Sql.UnitTesting.targets" Condition="$(VisualStudioVersion) != '15.0' And '$(SQLDBExtensionsRefPath)' == ''" />
<PropertyGroup>
<SSDTUnitTestPath Condition="'$(SSDTUnitTestPath)' == ''">$(VsInstallRoot)\Common7\IDE\Extensions\Microsoft\SQLDB</SSDTUnitTestPath>
</PropertyGroup>
<PropertyGroup>
<SSDTPath Condition="'$(SSDTPath)' == ''">$(VsInstallRoot)\Common7\IDE\Extensions\Microsoft\SQLDB\DAC\130</SSDTPath>
</PropertyGroup>
<PropertyGroup>
<VisualStudioVersion Condition="'$(VisualStudioVersion)' == ''">10.0</VisualStudioVersion>
</PropertyGroup>
<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
<IsPackable>false</IsPackable>
<RootNamespace>Sarand.BusinessMgr</RootNamespace>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.10.0" />
<PackageReference Include="Moq" Version="4.20.70" />
<PackageReference Include="MSTest.TestAdapter" Version="3.4.3" />
<PackageReference Include="MSTest.TestFramework" Version="3.4.3" />
<PackageReference Include="coverlet.collector" Version="6.0.2">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
</ItemGroup>
<ItemGroup Condition="$(VisualStudioVersion) == '16.0'">
<Reference Include="Microsoft.Data.Tools.Schema.Sql, Version=15.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
<HintPath>$(SSDTPath)\Microsoft.Data.Tools.Schema.Sql.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="Microsoft.Data.Tools.Schema.Sql.UnitTesting, Version=16.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
<HintPath>$(SSDTUnitTestPath)\Microsoft.Data.Tools.Schema.Sql.UnitTesting.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="Microsoft.Data.Tools.Schema.Sql.UnitTestingAdapter, Version=16.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
<HintPath>$(SSDTUnitTestPath)\Microsoft.Data.Tools.Schema.Sql.UnitTestingAdapter.dll</HintPath>
<Private>True</Private>
</Reference>
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\..\Common-VS\Workspace\SacsCoreWeb\SacsCoreWeb.csproj" />
<ProjectReference Include="..\..\..\Common-VS\Workspace\SacsStandardAccounts\SacsStandardAccounts.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="..\SaBusinessMgrCoreWebPortal\SaBusinessMgrCoreWebPortal.csproj" />
<ProjectReference Include="..\SaBusMgrCoreProcs\SaBusMgrCoreProcs.csproj" />
<ProjectReference Include="..\SaBusMgrCoreWeb\SaBusMgrCoreWeb.csproj" />
<ProjectReference Include="..\SaBusMgrStandardBase\SaBusMgrStandardBase.csproj" />
<ProjectReference Include="..\SaBusMgrStandardDbas\SaBusMgrStandardDbas.csproj" />
</ItemGroup>
<PropertyGroup>
<SsdtUnitTestVersion>3.1</SsdtUnitTestVersion>
</PropertyGroup>
<Import Project="$(SQLDBExtensionsRefPath)\Microsoft.Data.Tools.Schema.Sql.UnitTesting.targets" Condition="$(VisualStudioVersion) != '15.0' And '$(SQLDBExtensionsRefPath)' != ''" />
<Import Project="$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)\SSDT\Microsoft.Data.Tools.Schema.Sql.UnitTesting.targets" Condition="$(VisualStudioVersion) != '15.0' And '$(SQLDBExtensionsRefPath)' == ''" />
</Project>
@@ -37,7 +37,7 @@ namespace Sarand.BusinessMgr.Test.Net.Core.Web.Portal.App
Mock<IConfiguration> mockConfiguration = new Mock<IConfiguration>();
Mock<IWebHostEnvironment> mockEnvironment = new Mock<IWebHostEnvironment>();
mockEnvironment.Setup(m => m.EnvironmentName).Returns(Environments.Development);
mockEnvironment.Setup(m => m.ContentRootPath).Returns(@"D:\Sarand\Projects\Users\AHoward\VS2019\BusinessMgr\Workspace\SaBusinessMgrCoreWebPortal");
mockEnvironment.Setup(m => m.ContentRootPath).Returns(@"D:\Sarand\Projects\Users\AHoward\VS2022\BusinessMgr\Workspace\SaBusinessMgrCoreWebPortal");
App = new SbmApplication_Portal_Test(mockConfiguration.Object, mockEnvironment.Object);
// Session configuration.
@@ -40,13 +40,16 @@ namespace Sarand.BusinessMgr.Test.Net.Core.Web.Portal.App
SbmAccountMgr accountMgr = Internals.AccountMgr;
Assert.IsNotNull(accountMgr);
// Database context.
String dbIdentifier = "SBMDB4321";
// Security item.
SaSecurityItem securityItemUser = securityConsole.GetUserByEmail("andy@sarand.com");
Assert.IsNotNull(securityItemUser);
// User account.
SbmAffiliate affiliateUser = accountMgr.GetAffiliateByUserId(securityItemUser.Id);
Assert.IsNotNull(affiliateUser );
Assert.IsNotNull(affiliateUser);
// Organisations.
List<SysIdString> supervisedOrganisations = accountMgr.GetAffiliatesSupervisedBy(affiliateUser.Id);
@@ -62,7 +65,7 @@ namespace Sarand.BusinessMgr.Test.Net.Core.Web.Portal.App
// Populate User Session.
SbmSessionData_Portal sessionData = new SbmSessionData_Portal(SessionContext, securityItemUser, affiliateUser, supervisedOrganisations, associationOrganisation, affiliateOrganisation);
SbmSessionData_Portal sessionData = new SbmSessionData_Portal(SessionContext, dbIdentifier, securityItemUser, affiliateUser, supervisedOrganisations, associationOrganisation, affiliateOrganisation);
Assert.IsNotNull(sessionData);
// Validate user session.
@@ -88,13 +91,16 @@ namespace Sarand.BusinessMgr.Test.Net.Core.Web.Portal.App
Assert.IsNotNull(sessionDataJson);
// Deserialise data.
SbmSessionData_Web sessionDataDeserialized = JsonConvert.DeserializeObject<SbmSessionData_Portal>(sessionDataJson, SbmController_Base.SbmSessionDataJsonSerializerSettings);
SbmSessionData_Portal sessionDataDeserialized = JsonConvert.DeserializeObject<SbmSessionData_Portal>(sessionDataJson, SbmController_Base.SbmSessionDataJsonSerializerSettings);
Assert.IsNotNull(sessionDataDeserialized);
// Validate deserialised data.
Assert.IsNotNull(sessionDataDeserialized.User);
// Database context.
Assert.AreEqual(dbIdentifier, sessionDataDeserialized.DbIdentifier);
// Security item.
Assert.AreEqual(securityItemUser.Id, sessionDataDeserialized.User.SecurityItemId);
Assert.AreEqual(securityItemUser.EmailAddress, sessionDataDeserialized.User.EmailAddress);
@@ -29,9 +29,12 @@ namespace Sarand.BusinessMgr.Net.Core.Web.Control.App
private PropertySet propertySet;
private Dictionary<String, SbmAppConfig> appConfigs = new Dictionary<string, SbmAppConfig>();
private Dictionary<String, SbmEmailConfig> emailConfigs = new Dictionary<string, SbmEmailConfig>();
ushort sessionContextIdx_Control = 0;
// Singular databases.
private ISacsDatabase controlDatabase = null;
private ISacsDatabase templateDatabase = null;
// Session contexts.
private static ushort sessionContextIdx_Next = 100;
private ushort sessionContextIdx_Control = 0;
private class SessionContextData
{
public SessionContextData(SbmSessionContext_Web sessionContext)
@@ -39,8 +42,8 @@ namespace Sarand.BusinessMgr.Net.Core.Web.Control.App
SessionContext = sessionContext;
}
public SbmSessionContext_Web SessionContext { get; }
public ISacsDatabase ControlDatabase { get; set; }
public ISacsDatabase BusMgrDatabase { get; set; }
public List<string> DbIdentifiers { get; } = new List<string>();
public ISacsDatabase BusMgrDatabase { get; set; }
}
private readonly Dictionary<ushort, SessionContextData> sessionContexts = new Dictionary<ushort, SessionContextData>();
@@ -58,7 +61,7 @@ namespace Sarand.BusinessMgr.Net.Core.Web.Control.App
catch (Exception exc)
{
SaLogger.Error(GetType().Name + ".CTR " + ExceptionUtil.GetErrorStateMessage(exc));
throw exc;
throw;
}
}
@@ -76,11 +79,19 @@ namespace Sarand.BusinessMgr.Net.Core.Web.Control.App
{
return sessionContexts.GetValue(index, null)?.SessionContext ?? null;
}
public override ISacsDatabase GetControlDatabase(SbmSessionContext sessionContext)
public override ISacsDatabase GetControlDatabase()
{
return sessionContexts.GetValue(sessionContext.Index, null)?.ControlDatabase;
return controlDatabase;
}
public override ISacsDatabase GetBusMgrDatabase(SbmSessionContext sessionContext)
public override ISacsDatabase GetTemplateDatabase()
{
return templateDatabase;
}
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;
}
@@ -136,29 +147,46 @@ namespace Sarand.BusinessMgr.Net.Core.Web.Control.App
LogApplicationStart(fn);
// Control database connection.
SacsDatabaseConsole.OdbcConfig config = new SacsDatabaseConsole.OdbcConfig();
config.location = AppConfig.ControlDatabase.Location;
config.user = AppConfig.ControlDatabase.User;
config.pword = AppConfig.ControlDatabase.Password;
SacsDatabase controlDatabase = SacsDatabaseConsole.OpenOdbcDatabase(config);
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);
// BusinessMgr database connection.
config = new SacsDatabaseConsole.OdbcConfig();
config.location = AppConfig.BusMgrDatabase.Location;
config.user = AppConfig.BusMgrDatabase.User;
config.pword = AppConfig.BusMgrDatabase.Password;
SacsDatabase busMgrDatabase = SacsDatabaseConsole.OpenOdbcDatabase(config);
if (busMgrDatabase.HasErrorState())
SaLogger.Warn(fn + " BusMgr Database initialisation error: " + busMgrDatabase.ErrorStateMessage);
// Template database connection.
if (AppConfig.TemplateDatabase != null)
{
odbcConfig = new SacsDatabaseConsole.OdbcConfig();
odbcConfig.location = AppConfig.TemplateDatabase.Location;
odbcConfig.user = AppConfig.TemplateDatabase.User;
odbcConfig.pword = AppConfig.TemplateDatabase.Password;
templateDatabase = SacsDatabaseConsole.OpenOdbcDatabase(odbcConfig);
if (templateDatabase.HasErrorState())
SaLogger.Warn(fn + " Template 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.WebControl));
sessionContextData.ControlDatabase = controlDatabase;
sessionContextData.BusMgrDatabase = busMgrDatabase;
sessionContexts.Add(sessionContextData.SessionContext.Index, sessionContextData);
// BusinessMgr database connections.
foreach (SbmAppConfig.DatabaseConfig dbConfig in AppConfig.BusMgrDatabases)
{
odbcConfig = new SacsDatabaseConsole.OdbcConfig();
odbcConfig.location = dbConfig.Location;
odbcConfig.user = dbConfig.User;
odbcConfig.pword = dbConfig.Password;
SacsDatabase busMgrDatabase = SacsDatabaseConsole.OpenOdbcDatabase(odbcConfig);
if (busMgrDatabase.HasErrorState())
SaLogger.Warn(fn + " BusMgr Database: " + busMgrDatabase.DatabaseName + ". Initialisation error: " + busMgrDatabase.ErrorStateMessage);
// Session context.
ushort sessionContextIdx = sessionContextIdx_Next++;
if (sessionContextIdx_Control < 1)
sessionContextIdx_Control = sessionContextIdx;
SessionContextData sessionContextData = new SessionContextData(new SbmSessionContext_Web(sessionContextIdx, Sbm.Application.WebControl));
sessionContextData.DbIdentifiers.AddRange(dbConfig.Identifiers);
sessionContextData.BusMgrDatabase = busMgrDatabase;
sessionContexts.Add(sessionContextData.SessionContext.Index, sessionContextData);
}
}
catch (Exception exc)
{
@@ -166,9 +194,8 @@ namespace Sarand.BusinessMgr.Net.Core.Web.Control.App
}
// Occasional startup processing.
SbmSessionContext sessionContext = GetSessionContext(sessionContextIdx_Control);
if (generateDatabaseDefinitions_Control)
GenerateDatabaseDefinition(sessionContext, SbmDbDfnGenerator.DatabaseInstance.Control, GetControlDatabase(sessionContext));
GenerateDatabaseDefinition(SbmDbDfnGenerator.DatabaseInstance.Control, GetControlDatabase());
if (generateDatabaseDefinitions_BusMgr)
{
// Use the 'template' database connection parameters.
@@ -176,24 +203,24 @@ namespace Sarand.BusinessMgr.Net.Core.Web.Control.App
config.location = AppConfig.TemplateDatabase.Location;
config.user = AppConfig.TemplateDatabase.User;
config.pword = AppConfig.TemplateDatabase.Password;
GenerateDatabaseDefinition(sessionContext, SbmDbDfnGenerator.DatabaseInstance.BusinessMgr, SacsDatabaseConsole.GenerateConnectionString(config));
GenerateDatabaseDefinition(SbmDbDfnGenerator.DatabaseInstance.BusinessMgr, GetTemplateDatabase());
}
}
//-------------------------------------------------------------------
// Generate database definition.
private void GenerateDatabaseDefinition(SbmSessionContext sessionContext, SbmDbDfnGenerator.DatabaseInstance dbInstance, ISacsDatabase db)
private void GenerateDatabaseDefinition(SbmDbDfnGenerator.DatabaseInstance dbInstance, ISacsDatabase db)
{
if (!generateDatabaseDefinitions_BusMgr || !SbmSystem.DebugMode || sessionContext == null) return;
if (!generateDatabaseDefinitions_BusMgr || !SbmSystem.DebugMode) return;
if (!StringUtil.EmptyOnNull(EnvironmentName).EqualsCI("Development")) return;
if (db == null || StringUtil.IsEmpty(db.ConnectionString)) return;
// Generate database definitions.
GenerateDatabaseDefinition(sessionContext, dbInstance, db.ConnectionString);
GenerateDatabaseDefinition(dbInstance, db.ConnectionString);
}
private void GenerateDatabaseDefinition(SbmSessionContext sessionContext, SbmDbDfnGenerator.DatabaseInstance dbInstance, String connectionString)
private void GenerateDatabaseDefinition(SbmDbDfnGenerator.DatabaseInstance dbInstance, String connectionString)
{
if (!generateDatabaseDefinitions_BusMgr || !SbmSystem.DebugMode || sessionContext == null) return;
if (!generateDatabaseDefinitions_BusMgr || !SbmSystem.DebugMode) return;
if (!StringUtil.EmptyOnNull(EnvironmentName).EqualsCI("Development")) return;
if (StringUtil.IsEmpty(connectionString)) return;
@@ -0,0 +1,41 @@
using Sarand.BusinessMgr.Net.Core.Web.App;
using Sarand.BusinessMgr.Net.Standard.Base.App;
using Sarand.BusinessMgr.Net.Standard.Base.Bus;
using Sarand.Net.Standard.Base.Bus;
using Sarand.Net.Standard.Base.Security;
using static Sarand.BusinessMgr.Net.Standard.Base.App.Sbm.Config.Database;
using System.Collections.Generic;
using System;
using Newtonsoft.Json;
namespace Sarand.BusinessMgr.Net.Core.Web.Control.App
{
//=======================================================================
public class SbmSessionData_Control : SbmSessionData_Web
{
//-------------------------------------------------------------------
// Construction.
[JsonConstructor]
private SbmSessionData_Control(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_Control(SbmSessionContext sessionContext) :
base(sessionContext)
{
}
//-------------------------------------------------------------------
protected override UserData CreateUserData(SbmSessionContext sessionContext, SaSecurityItem securityItemUser, SbmAffiliate affiliateUser, List<SysIdString> supervisedOrganisations)
{
return null;
}
//-------------------------------------------------------------------
}
//=======================================================================
}
@@ -1,5 +1,6 @@
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Logging;
using Sarand.BusinessMgr.Net.Core.Web.App;
using Sarand.BusinessMgr.Net.Core.Web.Control.Models;
using System;
using System.Collections.Generic;
@@ -9,29 +10,39 @@ using System.Threading.Tasks;
namespace Sarand.BusinessMgr.Net.Core.Web.Control.Controllers
{
public class HomeController : Controller
{
private readonly ILogger<HomeController> _logger;
//======================================================================
public class HomeController : SbmController_Control
{
//-------------------------------------------------------------------
// Data members.
public HomeController(ILogger<HomeController> logger)
{
_logger = logger;
//-------------------------------------------------------------------
// Construction.
public HomeController(SbmLocaliser localiser, ILogger<HomeController> logger) :
base(localiser, logger)
{
}
public IActionResult Index()
//-------------------------------------------------------------------
public IActionResult Index()
{
// Home page.
Model_Home model = new Model_Home(Localiser);
return View();
}
//-------------------------------------------------------------------
public IActionResult Privacy()
{
return View();
}
public IActionResult Privacy()
{
return View();
}
[ResponseCache(Duration = 0, Location = ResponseCacheLocation.None, NoStore = true)]
//-------------------------------------------------------------------
[ResponseCache(Duration = 0, Location = ResponseCacheLocation.None, NoStore = true)]
public IActionResult Error()
{
return View(new ErrorViewModel { RequestId = Activity.Current?.Id ?? HttpContext.TraceIdentifier });
}
}
//======================================================================
}
@@ -0,0 +1,35 @@
using Microsoft.AspNetCore.Mvc;
using Newtonsoft.Json;
using Sarand.BusinessMgr.Net.Core.Web.App;
using Sarand.BusinessMgr.Net.Core.Web.Control.App;
using Sarand.BusinessMgr.Net.Core.Web.Controllers;
using System;
namespace Sarand.BusinessMgr.Net.Core.Web.Control.Controllers
{
//======================================================================
public class SbmController_Control : SbmController_Base
{
//-------------------------------------------------------------------
// Data members.
//-------------------------------------------------------------------
// Protected construction.
protected SbmController_Control(SbmLocaliser localiser, Microsoft.Extensions.Logging.ILogger<SbmController_Control> logger) : base(localiser)
{
}
//-------------------------------------------------------------------
// Session data.
//-------------------------------------------------------------------
/// <summary>Online session data</summary>
protected SbmSessionData_Control SessionData_Control { get { return SessionData as SbmSessionData_Control; } }
protected override SbmSessionData_Web DeserializeSessionData(String sessionDataJson)
{
return JsonConvert.DeserializeObject<SbmSessionData_Control>(sessionDataJson, SbmSessionDataJsonSerializerSettings);
}
//-------------------------------------------------------------------
}
//======================================================================
}
@@ -0,0 +1,18 @@
using Sarand.BusinessMgr.Net.Core.Web.App;
namespace Sarand.BusinessMgr.Net.Core.Web.Control.Models
{
//=======================================================================
public class Model_Home : SbmModel_Control
{
//-------------------------------------------------------------------
// Construction.
public Model_Home(SbmLocaliser localiser) :
base(localiser)
{
}
//-------------------------------------------------------------------
}
//=======================================================================
}
@@ -0,0 +1,19 @@
using Sarand.BusinessMgr.Net.Core.Web.App;
using Sarand.BusinessMgr.Net.Core.Web.Models;
namespace Sarand.BusinessMgr.Net.Core.Web.Control.Models
{
//=======================================================================
public class SbmModel_Control : SbmModel_Base
{
//-------------------------------------------------------------------
// Protected construction.
protected SbmModel_Control(SbmLocaliser localiser) :
base(localiser)
{
}
//-------------------------------------------------------------------
}
//=======================================================================
}
@@ -1,52 +1,52 @@
<Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup>
<TargetFramework>netcoreapp3.1</TargetFramework>
<CopyRefAssembliesToPublishDirectory>false</CopyRefAssembliesToPublishDirectory>
<RootNamespace>Sarand.BusinessMgr.Net.Core.Web</RootNamespace>
</PropertyGroup>
<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
<CopyRefAssembliesToPublishDirectory>false</CopyRefAssembliesToPublishDirectory>
<RootNamespace>Sarand.BusinessMgr.Net.Core.Web</RootNamespace>
</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>
<Folder Include="Control\Consoles\" />
</ItemGroup>
<ItemGroup>
<Folder Include="Control\Consoles\" />
</ItemGroup>
<ItemGroup>
<Content Update="appsettings.Development.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
<ExcludeFromSingleFile>true</ExcludeFromSingleFile>
<CopyToPublishDirectory>PreserveNewest</CopyToPublishDirectory>
</Content>
<Content Update="appsettings.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
<ExcludeFromSingleFile>true</ExcludeFromSingleFile>
<CopyToPublishDirectory>PreserveNewest</CopyToPublishDirectory>
</Content>
<Content Update="appsettings.Machine.Sarand107.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
<ExcludeFromSingleFile>true</ExcludeFromSingleFile>
<CopyToPublishDirectory>PreserveNewest</CopyToPublishDirectory>
</Content>
<Content Update="appsettings.Production.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
<ExcludeFromSingleFile>true</ExcludeFromSingleFile>
<CopyToPublishDirectory>PreserveNewest</CopyToPublishDirectory>
</Content>
</ItemGroup>
<ItemGroup>
<Content Update="appsettings.Development.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
<ExcludeFromSingleFile>true</ExcludeFromSingleFile>
<CopyToPublishDirectory>PreserveNewest</CopyToPublishDirectory>
</Content>
<Content Update="appsettings.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
<ExcludeFromSingleFile>true</ExcludeFromSingleFile>
<CopyToPublishDirectory>PreserveNewest</CopyToPublishDirectory>
</Content>
<Content Update="appsettings.Machine.Sarand110.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
<ExcludeFromSingleFile>true</ExcludeFromSingleFile>
<CopyToPublishDirectory>PreserveNewest</CopyToPublishDirectory>
</Content>
<Content Update="appsettings.Production.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
<ExcludeFromSingleFile>true</ExcludeFromSingleFile>
<CopyToPublishDirectory>PreserveNewest</CopyToPublishDirectory>
</Content>
</ItemGroup>
</Project>
@@ -1,4 +1,12 @@
<!DOCTYPE html>
@model SbmModel_Control
@inject Microsoft.AspNetCore.Antiforgery.IAntiforgery AntiForgery;
@inject SbmLocaliser Localiser
@*
Application: BusinessMgr Web Control
Purpose: Page template.
*@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
@@ -34,7 +42,7 @@
<footer class="border-top footer text-muted">
<div class="container">
&copy; 2021 - Sarand BusinessMgr
@(Model.CopyrightNotice) - Sarand BusinessMgr
</div>
</footer>
<script src="~/lib/jquery/dist/jquery.min.js"></script>
@@ -1,3 +1,4 @@
@using Sarand.BusinessMgr.Net.Core.Web.Control
@using Sarand.BusinessMgr.Net.Core.Web.App
@using Sarand.BusinessMgr.Net.Core.Web.Control
@using Sarand.BusinessMgr.Net.Core.Web.Control.Models
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers
@@ -1,48 +0,0 @@
{
"AppConfigs": {
"SaBusMgr-Dev": {
"Environments": [ "Development" ],
"Instance": "Dev",
"ControlDatabase": {
"Location": "MsSql:Sarand107\\SQL2016.SaBusinessMgr_Control",
"User": "SaBusMgr-Control",
"Password": "sbm-c0Ntrul-dev-2021"
},
"BusMgrDatabases": [
{
"Code": "Sarand",
"Location": "MsSql:Sarand106\\SQL2014.SaBusinessMgr_Sarand"
},
{
"Code": "Cadix",
"Location": "MsSql:Sarand107\\SQL2016.SaBusinessMgr_Cadix",
"User": "SaBusMgr-Cadix",
"Password": "sbm-cul-dev-2021"
}
],
"BusMgrDatabase": {
"Code": "Cadix",
"Location": "MsSql:Sarand107\\SQL2016.SaBusinessMgr_Cadix",
"User": "SaBusMgr-Cadix",
"Password": "sbm-cul-dev-2021"
},
"TemplateDatabase": {
"Location": "MsSql:Sarand107\\SQL2016.SaBusinessMgr_Template",
"User": "SaBusMgr-Control",
"Password": "sbm-c0Ntrul-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\\WebControl"
}
}
}
}
@@ -0,0 +1,50 @@
{
"AppConfigs": {
"SaBusMgr-Dev": {
"Environments": [ "Development" ],
"Instance": "Dev",
"ControlDatabase": {
"Location": "MsSql:Roman\\SQL2016.SaBusinessMgr_Control",
"User": "sapphire",
"Password": "sarand"
},
"TemplateDatabase": {
"Location": "MsSql:Roman\\SQL2016.SaBusinessMgr_Template",
"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\\WebControl"
}
}
}
}
@@ -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);