diff --git a/SaBusMgrCoreProcs/Procs/Consoles/SbmProcessConsole.cs b/SaBusMgrCoreProcs/Procs/Consoles/SbmProcessConsole.cs index f0408cb..24d9be3 100644 --- a/SaBusMgrCoreProcs/Procs/Consoles/SbmProcessConsole.cs +++ b/SaBusMgrCoreProcs/Procs/Consoles/SbmProcessConsole.cs @@ -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 //------------------------------------------------------------------- } //======================================================================= - /// - /// Process Notificer for Process Console. - /// - class SbmProcessConsole_Notifier : ISacsProcessNotifier - { - public void OnStartProcess() - { - } - public void OnEndProcess() - { - } - public void SetProgressInfo(List progressInfo) - { - } - } - //======================================================================= } diff --git a/SaBusMgrCoreProcs/Procs/Interfaces/SbmInterfaceProcess.cs b/SaBusMgrCoreProcs/Procs/Interfaces/SbmInterfaceProcess.cs index 4158317..e00aeb8 100644 --- a/SaBusMgrCoreProcs/Procs/Interfaces/SbmInterfaceProcess.cs +++ b/SaBusMgrCoreProcs/Procs/Interfaces/SbmInterfaceProcess.cs @@ -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; } diff --git a/SaBusMgrCoreProcs/Procs/Interfaces/SbmInterfaceProcess_Commodity_Export.cs b/SaBusMgrCoreProcs/Procs/Interfaces/SbmInterfaceProcess_Commodity_Export.cs index d704829..790a41e 100644 --- a/SaBusMgrCoreProcs/Procs/Interfaces/SbmInterfaceProcess_Commodity_Export.cs +++ b/SaBusMgrCoreProcs/Procs/Interfaces/SbmInterfaceProcess_Commodity_Export.cs @@ -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; } //------------------------------------------------------------------- /// Construct the commodity filter parameters. - 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 progressMessages = new List(); @@ -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 GetInterfaceItemOptionDfns () + public List GetInterfaceItemOptionDfns() { InitOptionDefinitions(); List interfaceOptionDfns = new List(); @@ -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, } /// Configure processing options. - public static List GetProcessingOptions (ISbmUserContext userContext, SaInterface interfaceObj) + public static List GetProcessingOptions(ISbmUserContext userContext, SaInterface interfaceObj) { List optionGroups = new List(); @@ -1088,7 +1089,7 @@ namespace Sarand.BusinessMgr.Net.Core.Procs.Interfaces //------------------------------------------------------------------- /// Process interface using selected options. - 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) { diff --git a/SaBusMgrCoreProcs/Procs/Procs/SbmProcess_Base-FTP.cs b/SaBusMgrCoreProcs/Procs/Procs/SbmProcess_Base-FTP.cs index 90469f8..d0a5ad7 100644 --- a/SaBusMgrCoreProcs/Procs/Procs/SbmProcess_Base-FTP.cs +++ b/SaBusMgrCoreProcs/Procs/Procs/SbmProcess_Base-FTP.cs @@ -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 { diff --git a/SaBusMgrCoreProcs/Procs/Procs/SbmProcess_Base.cs b/SaBusMgrCoreProcs/Procs/Procs/SbmProcess_Base.cs index 957c721..07ff457 100644 --- a/SaBusMgrCoreProcs/Procs/Procs/SbmProcess_Base.cs +++ b/SaBusMgrCoreProcs/Procs/Procs/SbmProcess_Base.cs @@ -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; } diff --git a/SaBusMgrCoreProcs/Procs/Procs/SbmProcess_Commodity_Export.cs b/SaBusMgrCoreProcs/Procs/Procs/SbmProcess_Commodity_Export.cs index 891680d..6adc063 100644 --- a/SaBusMgrCoreProcs/Procs/Procs/SbmProcess_Commodity_Export.cs +++ b/SaBusMgrCoreProcs/Procs/Procs/SbmProcess_Commodity_Export.cs @@ -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); } } diff --git a/SaBusMgrCoreProcs/SaBusMgrCoreProcs.csproj b/SaBusMgrCoreProcs/SaBusMgrCoreProcs.csproj index 709bf4f..75c228f 100644 --- a/SaBusMgrCoreProcs/SaBusMgrCoreProcs.csproj +++ b/SaBusMgrCoreProcs/SaBusMgrCoreProcs.csproj @@ -1,23 +1,19 @@ - - netcoreapp3.1 - Sarand.BusinessMgr.Net.Core - + + net6.0 + Sarand.BusinessMgr.Net.Core + - - - - - + + + + + - - - - - - - - + + + + diff --git a/SaBusMgrCoreWeb/SaBusMgrCoreWeb.csproj b/SaBusMgrCoreWeb/SaBusMgrCoreWeb.csproj index b65a56d..7610589 100644 --- a/SaBusMgrCoreWeb/SaBusMgrCoreWeb.csproj +++ b/SaBusMgrCoreWeb/SaBusMgrCoreWeb.csproj @@ -1,25 +1,25 @@ - - netcoreapp3.1 - Sarand.BusinessMgr.Net.Core - + + net6.0 + Sarand.BusinessMgr.Net.Core + - - - + + + - - - - - - - - + + + + + + + + - - - + + + diff --git a/SaBusMgrCoreWeb/Web/App/SbmAppConfig.cs b/SaBusMgrCoreWeb/Web/App/SbmAppConfig.cs index 4bee073..dd562b5 100644 --- a/SaBusMgrCoreWeb/Web/App/SbmAppConfig.cs +++ b/SaBusMgrCoreWeb/Web/App/SbmAppConfig.cs @@ -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 Identifiers { get; } = new List(); 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 BusMgrDatabases { get; } = new List(); // Documents. public class DocumentsConfig { diff --git a/SaBusMgrCoreWeb/Web/App/SbmApplication_Web.cs b/SaBusMgrCoreWeb/Web/App/SbmApplication_Web.cs index 0babb5e..293df56 100644 --- a/SaBusMgrCoreWeb/Web/App/SbmApplication_Web.cs +++ b/SaBusMgrCoreWeb/Web/App/SbmApplication_Web.cs @@ -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); diff --git a/SaBusMgrCoreWeb/Web/App/SbmSessionData_Web.cs b/SaBusMgrCoreWeb/Web/App/SbmSessionData_Web.cs index 4e8b265..24b3736 100644 --- a/SaBusMgrCoreWeb/Web/App/SbmSessionData_Web.cs +++ b/SaBusMgrCoreWeb/Web/App/SbmSessionData_Web.cs @@ -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 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 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; } } //------------------------------------------------------------------- diff --git a/SaBusMgrCoreWeb/Web/App/SbmUserContext_Web.cs b/SaBusMgrCoreWeb/Web/App/SbmUserContext_Web.cs index 29d5fc8..e8c6b7b 100644 --- a/SaBusMgrCoreWeb/Web/App/SbmUserContext_Web.cs +++ b/SaBusMgrCoreWeb/Web/App/SbmUserContext_Web.cs @@ -9,7 +9,7 @@ namespace Sarand.BusinessMgr.Net.Core.Web.App public class SbmUserContext_Web : SbmUserContext { //------------------------------------------------------------------- - // Data memebrs. + // Data members. //------------------------------------------------------------------- // Construction. diff --git a/SaBusMgrCoreWeb/Web/Controllers/SbmController_Base.cs b/SaBusMgrCoreWeb/Web/Controllers/SbmController_Base.cs index 2a50682..8e11f5a 100644 --- a/SaBusMgrCoreWeb/Web/Controllers/SbmController_Base.cs +++ b/SaBusMgrCoreWeb/Web/Controllers/SbmController_Base.cs @@ -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; } - /// - /// Indicate whether session data should be stored by action. - /// Any action that might modify the session data must store it. - /// Some actions such as scoring a password's complexity don't need to. - /// - public virtual bool StoreSessionData { get; protected set; } = true; - // Culture. - private string LanguageIso2 - { - get - { - var feature = HttpContext.Features.Get(); - 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. - //------------------------------------------------------------------- - /// Retrieve the value from the named request cookie. - public String GetRequestCookie(String key) - { - if (Request.Cookies.ContainsKey(key)) - return Request.Cookies[key]; - return null; - } - /// Set the value against the named response cookie. - 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) - { - } - - //--------------------------------------------------------------- - /// - /// Common item response data properties. - /// - Null values are ignored by the JSON serialiser. - /// - Return null for false boolean values where possible. - /// - Return null for blank string values where possible. - /// - 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 controlValues = new Dictionary(); - public List 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)); - } - } - /// Assign the error to the control by name and return this response data. - public AjaxResponse_Data SetControlError(String controlName, String error) - { - return SetControlError(controlName, error, this); - } - /// - /// Assign the error to the control by name and return the specified value. - /// - Allows alternative return values to be specified (eg. 'false') to avoid separate return statement in calling routine. - /// - public T SetControlError(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); - } - - //--------------------------------------------------------------- - } - //------------------------------------------------------------------- } //======================================================================= diff --git a/SaBusMgrCoreWeb/Web/Helpers/SbmSessionContextHelper_Web.cs b/SaBusMgrCoreWeb/Web/Helpers/SbmSessionContextHelper_Web.cs index 8fdd440..af89d94 100644 --- a/SaBusMgrCoreWeb/Web/Helpers/SbmSessionContextHelper_Web.cs +++ b/SaBusMgrCoreWeb/Web/Helpers/SbmSessionContextHelper_Web.cs @@ -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. /// - public static List GetSessionContextIdxsForUsersBusMgrDatabase(String emailAddress) + public static List GetSessionContextIdxsForContextUserEmail(String dbIdentifier, String emailAddress) { - String fn = className + ".GetSessionContextIdxForUsersBusMgrDatabase"; String alias_SecurityItem = "SecItm"; String alias_Affiliate = "Affil"; List sessionContextIdxs = new List(); 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 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; diff --git a/SaBusMgrCoreWeb/Web/Middleware/SbmAuthenticationHandler.cs b/SaBusMgrCoreWeb/Web/Middleware/SbmAuthenticationHandler.cs index b94dd76..f9a2969 100644 --- a/SaBusMgrCoreWeb/Web/Middleware/SbmAuthenticationHandler.cs +++ b/SaBusMgrCoreWeb/Web/Middleware/SbmAuthenticationHandler.cs @@ -36,6 +36,7 @@ namespace Sarand.BusinessMgr.Net.Core.Web.Middleware protected override async Task HandleAuthenticateAsync() { Context.Response.StatusCode = (int)HttpStatusCode.Unauthorized; + await Task.CompletedTask; return AuthenticateResult.NoResult(); } diff --git a/SaBusMgrCoreWeb/Web/Middleware/SbmModelBinder.cs b/SaBusMgrCoreWeb/Web/Middleware/SbmModelBinder.cs index 0b17859..4305728 100644 --- a/SaBusMgrCoreWeb/Web/Middleware/SbmModelBinder.cs +++ b/SaBusMgrCoreWeb/Web/Middleware/SbmModelBinder.cs @@ -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); } //------------------------------------------------------------------- diff --git a/SaBusMgrCoreWeb/Web/Middleware/ValidateAntiForgeryTokenMiddleware.cs b/SaBusMgrCoreWeb/Web/Middleware/ValidateAntiForgeryTokenMiddleware.cs index 46bf307..88d999c 100644 --- a/SaBusMgrCoreWeb/Web/Middleware/ValidateAntiForgeryTokenMiddleware.cs +++ b/SaBusMgrCoreWeb/Web/Middleware/ValidateAntiForgeryTokenMiddleware.cs @@ -60,7 +60,7 @@ namespace Sarand.BusinessMgr.Net.Core.Web.Middleware catch (Exception exc) { SaLogger.Warn(fn + " " + ExceptionUtil.GetLogMessage(exc)); - throw exc; + throw; } } } diff --git a/SaBusMgrCoreWeb/Web/Models/SbmModel_Base.cs b/SaBusMgrCoreWeb/Web/Models/SbmModel_Base.cs index cd1de65..22936f4 100644 --- a/SaBusMgrCoreWeb/Web/Models/SbmModel_Base.cs +++ b/SaBusMgrCoreWeb/Web/Models/SbmModel_Base.cs @@ -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 infoMessages = new List(); - private readonly List warningMessages = new List(); - private readonly List errorMessages = new List(); //------------------------------------------------------------------- // 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 //------------------------------------------------------------------- /// - /// Form control value max length. + /// Form control value max length - Security tables. + /// In many cases the database columns allow plenty of extra space for the value, in case they need to be encrypted. + /// Hence, several cases where the value entered in the control isn't allowed the full database length. + /// + 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; + } + + /// + /// Form control value max length - BusinessMgr tables. /// In many cases the database columns allow plenty of extra space for the value, in case they need to be encrypted. /// Hence, several cases where the value entered in the control isn't allowed the full database length. /// @@ -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 /// List of Commodity Types for dropdown. public IEnumerable 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 AdditionalFiles_CSS { get; set; } = new List(); - public List AdditionalFiles_JS { get; set; } = new List(); - - //------------------------------------------------------------------- - // 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. /// @@ -280,38 +244,9 @@ namespace Sarand.BusinessMgr.Net.Core.Web.Models /// Equivalent to the Site index on the login form. /// Populates value on logout form to identify default site for next login. /// - 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 InfoMessages { get { return new List(infoMessages); } } - public List WarningMessages { get { return new List(warningMessages); } } - public List ErrorMessages { get { return new List(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); - } - //------------------------------------------------------------------- } //======================================================================= diff --git a/SaBusMgrStandardBase/Base/App/Sbm.cs b/SaBusMgrStandardBase/Base/App/Sbm.cs index da9c588..53e99a1 100644 --- a/SaBusMgrStandardBase/Base/App/Sbm.cs +++ b/SaBusMgrStandardBase/Base/App/Sbm.cs @@ -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"; diff --git a/SaBusMgrStandardBase/Base/App/SbmApplication.cs b/SaBusMgrStandardBase/Base/App/SbmApplication.cs index e6fd2d8..161229e 100644 --- a/SaBusMgrStandardBase/Base/App/SbmApplication.cs +++ b/SaBusMgrStandardBase/Base/App/SbmApplication.cs @@ -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 GetDbIdentifiers(SbmSessionContext sessionContext); + public abstract ISacsDatabase GetBusMgrDatabase(SbmSessionContext sessionContext); public IDatabaseInfo CreateBusMgrDbInfo(SbmSessionContext sessionContext) { return GetBusMgrDatabase(sessionContext).CreateDbInfo(); diff --git a/SaBusMgrStandardBase/Base/App/SbmInternals.cs b/SaBusMgrStandardBase/Base/App/SbmInternals.cs index 13967d9..0978234 100644 --- a/SaBusMgrStandardBase/Base/App/SbmInternals.cs +++ b/SaBusMgrStandardBase/Base/App/SbmInternals.cs @@ -115,6 +115,7 @@ namespace Sarand.BusinessMgr.Net.Standard.Base.App return new List(systemCodeCache.GetValueOrDefault(systemCode, new List())); } public List AffiliateTypeCodes { get { return GetCodesOfType(Sbm.SystemCode.AffiliateType); } } + public List AssociationTypeCodes { get { return GetCodesOfType(Sbm.SystemCode.AssociationType); } } public List CommodityTypeCodes { get { return GetCodesOfType(Sbm.SystemCode.CommodityType); } } //------------------------------------------------------------------- diff --git a/SaBusMgrStandardBase/Base/App/SbmResourceConsole.cs b/SaBusMgrStandardBase/Base/App/SbmResourceConsole.cs index 47105b5..3712e97 100644 --- a/SaBusMgrStandardBase/Base/App/SbmResourceConsole.cs +++ b/SaBusMgrStandardBase/Base/App/SbmResourceConsole.cs @@ -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 ""; } diff --git a/SaBusMgrStandardBase/Base/App/SbmSessionData.cs b/SaBusMgrStandardBase/Base/App/SbmSessionData.cs index d8323d9..b2715df 100644 --- a/SaBusMgrStandardBase/Base/App/SbmSessionData.cs +++ b/SaBusMgrStandardBase/Base/App/SbmSessionData.cs @@ -31,27 +31,39 @@ namespace Sarand.BusinessMgr.Net.Standard.Base.App userData = CreateUserData(SessionContext, null, null, null); organisationData = new OrganisationData(null, null); } - /// - /// Primary constructor - on user login. - /// SessionContext and UserAccount must be supplied. - /// Roles list is optional. - /// - protected SbmSessionData(SbmSessionContext sessionContext, SaSecurityItem securityItemUser, SbmAffiliate affiliateUser, List supervisedOrganisations, SbmAssociation associationOrganisation, SbmAffiliate affiliateOrganisation) - { - SessionIdx = ++sessionIdxRoot; - ItemIdx = 1000; - SessionContext = sessionContext; - userData = CreateUserData(SessionContext, securityItemUser, affiliateUser, supervisedOrganisations); - organisationData = new OrganisationData(associationOrganisation, affiliateOrganisation); - } + /// + /// Plain constructor - Contorl application. + /// SessionContext must be supplied. + /// + protected SbmSessionData(SbmSessionContext sessionContext) + { + SessionIdx = ++sessionIdxRoot; + ItemIdx = 1000; + SessionContext = sessionContext; + } + /// + /// Primary constructor - on user login. + /// SessionContext and UserAccount must be supplied. + /// Roles list is optional. + /// + protected SbmSessionData(SbmSessionContext sessionContext, SaSecurityItem securityItemUser, SbmAffiliate affiliateUser, List 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] diff --git a/SaBusMgrStandardBase/Base/Bus/SbmAssociation.cs b/SaBusMgrStandardBase/Base/Bus/SbmAssociation.cs index c285282..f726508 100644 --- a/SaBusMgrStandardBase/Base/Bus/SbmAssociation.cs +++ b/SaBusMgrStandardBase/Base/Bus/SbmAssociation.cs @@ -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; } + /// Generic search value - code, name, phone, website, email, address etc. + 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; } /// Status category. 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; } } } //======================================================================= diff --git a/SaBusMgrStandardBase/Base/Core/ISbmUserContext.cs b/SaBusMgrStandardBase/Base/Core/ISbmUserContext.cs index ad92140..e7ffe9b 100644 --- a/SaBusMgrStandardBase/Base/Core/ISbmUserContext.cs +++ b/SaBusMgrStandardBase/Base/Core/ISbmUserContext.cs @@ -38,7 +38,7 @@ namespace Sarand.BusinessMgr.Net.Standard.Base.Core IUserData User { get; } bool ValidUser { get; } - /// Database upfate info - user session specific properties for database inserts/updates. + /// Database update info - user session specific properties for database inserts/updates. DbUpdateInfo DbUpdateInfo { get; } //------------------------------------------------------------------- diff --git a/SaBusMgrStandardBase/Base/Mgr/SbmAccountMgr_Association.cs b/SaBusMgrStandardBase/Base/Mgr/SbmAccountMgr_Association.cs index 3638e82..3730062 100644 --- a/SaBusMgrStandardBase/Base/Mgr/SbmAccountMgr_Association.cs +++ b/SaBusMgrStandardBase/Base/Mgr/SbmAccountMgr_Association.cs @@ -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); } diff --git a/SaBusMgrStandardBase/SaBusMgrStandardBase.csproj b/SaBusMgrStandardBase/SaBusMgrStandardBase.csproj index 5c28877..7bdaff0 100644 --- a/SaBusMgrStandardBase/SaBusMgrStandardBase.csproj +++ b/SaBusMgrStandardBase/SaBusMgrStandardBase.csproj @@ -1,7 +1,7 @@ - netstandard2.1 + net6.0 Sarand.BusinessMgr.Net.Standard diff --git a/SaBusMgrStandardDbas/Dbas/Mgr/SbmAccountMgr_Dbas-Association.cs b/SaBusMgrStandardDbas/Dbas/Mgr/SbmAccountMgr_Dbas-Association.cs index 859a7fb..f151087 100644 --- a/SaBusMgrStandardDbas/Dbas/Mgr/SbmAccountMgr_Dbas-Association.cs +++ b/SaBusMgrStandardDbas/Dbas/Mgr/SbmAccountMgr_Dbas-Association.cs @@ -1318,6 +1318,7 @@ namespace Sarand.BusinessMgr.Net.Standard.Dbas.Mgr { List filter = new List(); + //--------------------------------------------------------------- // 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 affiliateFilter = new List(); 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 addressFilter = new List(); + String addressPartial = DbQuery.FormatForLike(StringUtil.EmptyOnNull(prm.AddressPartial)); + if (!StringUtil.IsEmpty(addressPartial)) { List addressCols = new List() { @@ -1427,45 +1450,133 @@ namespace Sarand.BusinessMgr.Net.Standard.Dbas.Mgr SbmDbDfn.ColumnIdx.Address_Province, SbmDbDfn.ColumnIdx.Address_Contact, }; - List filterAddress = new List(); - 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 searchFilter = new List(); + foreach (String rawSearchValue in prm.Search.Split(new char[] { ' ', ',', ';' })) + { + String searchValue = DbQuery.FormatForLike(StringUtil.TrimAll(rawSearchValue)); + if (StringUtil.IsEmpty(searchValue)) continue; + + // Association filters. + List searchValueFilter = new List(); + 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 affiliateFilter = new List(); + 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 addressFilter = new List(); + List addressCols = new List() + { + 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 affiliateOwnerCols = new List() { 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 affiliateTargetCols = new List(); 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)); diff --git a/SaBusMgrStandardDbas/Dbas/Utils/QuickBooksDdDfnGenerator.cs b/SaBusMgrStandardDbas/Dbas/Utils/QuickBooksDdDfnGenerator.cs index 2e1d374..0b57c6c 100644 --- a/SaBusMgrStandardDbas/Dbas/Utils/QuickBooksDdDfnGenerator.cs +++ b/SaBusMgrStandardDbas/Dbas/Utils/QuickBooksDdDfnGenerator.cs @@ -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. diff --git a/SaBusMgrStandardDbas/Dbas/Utils/SbmDbDfnGenerator.cs b/SaBusMgrStandardDbas/Dbas/Utils/SbmDbDfnGenerator.cs index 94b0b54..3c81e1a 100644 --- a/SaBusMgrStandardDbas/Dbas/Utils/SbmDbDfnGenerator.cs +++ b/SaBusMgrStandardDbas/Dbas/Utils/SbmDbDfnGenerator.cs @@ -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. diff --git a/SaBusMgrStandardDbas/SaBusMgrStandardDbas.csproj b/SaBusMgrStandardDbas/SaBusMgrStandardDbas.csproj index c9dc80a..b8b544d 100644 --- a/SaBusMgrStandardDbas/SaBusMgrStandardDbas.csproj +++ b/SaBusMgrStandardDbas/SaBusMgrStandardDbas.csproj @@ -1,19 +1,19 @@ - - netstandard2.1 - Sarand.BusinessMgr.Net.Standard - + + net6.0 + Sarand.BusinessMgr.Net.Standard + - - - - - + + + + + - - - - + + + + diff --git a/SaBusMgrTest_Portal/SaBusMgrTest_Portal.csproj b/SaBusMgrTest_Portal/SaBusMgrTest_Portal.csproj index e69fbeb..cfb520a 100644 --- a/SaBusMgrTest_Portal/SaBusMgrTest_Portal.csproj +++ b/SaBusMgrTest_Portal/SaBusMgrTest_Portal.csproj @@ -1,54 +1,57 @@ - - $(VsInstallRoot)\Common7\IDE\Extensions\Microsoft\SQLDB - - - $(VsInstallRoot)\Common7\IDE\Extensions\Microsoft\SQLDB\DAC\130 - - - 10.0 - - - netcoreapp3.1 - false - Sarand.BusinessMgr - - - - - - - - - - - $(SSDTPath)\Microsoft.Data.Tools.Schema.Sql.dll - True - - - $(SSDTUnitTestPath)\Microsoft.Data.Tools.Schema.Sql.UnitTesting.dll - True - - - $(SSDTUnitTestPath)\Microsoft.Data.Tools.Schema.Sql.UnitTestingAdapter.dll - True - - - - - - - - - - - - - - - - 3.1 - - - + + $(VsInstallRoot)\Common7\IDE\Extensions\Microsoft\SQLDB + + + $(VsInstallRoot)\Common7\IDE\Extensions\Microsoft\SQLDB\DAC\130 + + + 10.0 + + + net6.0 + false + Sarand.BusinessMgr + + + + + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + + + $(SSDTPath)\Microsoft.Data.Tools.Schema.Sql.dll + True + + + $(SSDTUnitTestPath)\Microsoft.Data.Tools.Schema.Sql.UnitTesting.dll + True + + + $(SSDTUnitTestPath)\Microsoft.Data.Tools.Schema.Sql.UnitTestingAdapter.dll + True + + + + + + + + + + + + + + + + 3.1 + + + \ No newline at end of file diff --git a/SaBusMgrTest_Portal/Test/Net/Core/Web/Portal/App/SbmPortalApp_Tester_Base.cs b/SaBusMgrTest_Portal/Test/Net/Core/Web/Portal/App/SbmPortalApp_Tester_Base.cs index 2b8ee31..5f1b00c 100644 --- a/SaBusMgrTest_Portal/Test/Net/Core/Web/Portal/App/SbmPortalApp_Tester_Base.cs +++ b/SaBusMgrTest_Portal/Test/Net/Core/Web/Portal/App/SbmPortalApp_Tester_Base.cs @@ -37,7 +37,7 @@ namespace Sarand.BusinessMgr.Test.Net.Core.Web.Portal.App Mock mockConfiguration = new Mock(); Mock mockEnvironment = new Mock(); 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. diff --git a/SaBusMgrTest_Portal/Test/Net/Core/Web/Portal/App/SbmSessionData_Portal_Test.cs b/SaBusMgrTest_Portal/Test/Net/Core/Web/Portal/App/SbmSessionData_Portal_Test.cs index 3dccd0f..135a697 100644 --- a/SaBusMgrTest_Portal/Test/Net/Core/Web/Portal/App/SbmSessionData_Portal_Test.cs +++ b/SaBusMgrTest_Portal/Test/Net/Core/Web/Portal/App/SbmSessionData_Portal_Test.cs @@ -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 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(sessionDataJson, SbmController_Base.SbmSessionDataJsonSerializerSettings); + SbmSessionData_Portal sessionDataDeserialized = JsonConvert.DeserializeObject(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); diff --git a/SaBusinessMgrCoreWebControl/Control/App/SbmApplication_Control.cs b/SaBusinessMgrCoreWebControl/Control/App/SbmApplication_Control.cs index 3fbf103..60a20a8 100644 --- a/SaBusinessMgrCoreWebControl/Control/App/SbmApplication_Control.cs +++ b/SaBusinessMgrCoreWebControl/Control/App/SbmApplication_Control.cs @@ -29,9 +29,12 @@ namespace Sarand.BusinessMgr.Net.Core.Web.Control.App private PropertySet propertySet; private Dictionary appConfigs = new Dictionary(); private Dictionary emailConfigs = new Dictionary(); + 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 DbIdentifiers { get; } = new List(); + public ISacsDatabase BusMgrDatabase { get; set; } } private readonly Dictionary sessionContexts = new Dictionary(); @@ -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 GetDbIdentifiers(SbmSessionContext sessionContext) + { + return sessionContexts.GetValue(sessionContext.Index, null)?.DbIdentifiers ?? new List(); + } + 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; diff --git a/SaBusinessMgrCoreWebControl/Control/App/SbmSessionData_Control.cs b/SaBusinessMgrCoreWebControl/Control/App/SbmSessionData_Control.cs new file mode 100644 index 0000000..d88e350 --- /dev/null +++ b/SaBusinessMgrCoreWebControl/Control/App/SbmSessionData_Control.cs @@ -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) + { } + /// + /// SessionData construction. + /// SessionContext and UserAccount must be supplied. + /// Organisation is optional. + /// + public SbmSessionData_Control(SbmSessionContext sessionContext) : + base(sessionContext) + { + } + + //------------------------------------------------------------------- + protected override UserData CreateUserData(SbmSessionContext sessionContext, SaSecurityItem securityItemUser, SbmAffiliate affiliateUser, List supervisedOrganisations) + { + return null; + } + + //------------------------------------------------------------------- + } + //======================================================================= +} diff --git a/SaBusinessMgrCoreWebControl/Control/Controllers/HomeController.cs b/SaBusinessMgrCoreWebControl/Control/Controllers/HomeController.cs index 4cf14fa..9e18759 100644 --- a/SaBusinessMgrCoreWebControl/Control/Controllers/HomeController.cs +++ b/SaBusinessMgrCoreWebControl/Control/Controllers/HomeController.cs @@ -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 _logger; + //====================================================================== + public class HomeController : SbmController_Control + { + //------------------------------------------------------------------- + // Data members. - public HomeController(ILogger logger) - { - _logger = logger; + //------------------------------------------------------------------- + // Construction. + public HomeController(SbmLocaliser localiser, ILogger 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 }); } } + //====================================================================== } diff --git a/SaBusinessMgrCoreWebControl/Control/Controllers/SbmController_Control.cs b/SaBusinessMgrCoreWebControl/Control/Controllers/SbmController_Control.cs new file mode 100644 index 0000000..e38b7ea --- /dev/null +++ b/SaBusinessMgrCoreWebControl/Control/Controllers/SbmController_Control.cs @@ -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 logger) : base(localiser) + { + } + + //------------------------------------------------------------------- + // Session data. + //------------------------------------------------------------------- + /// Online session data + protected SbmSessionData_Control SessionData_Control { get { return SessionData as SbmSessionData_Control; } } + protected override SbmSessionData_Web DeserializeSessionData(String sessionDataJson) + { + return JsonConvert.DeserializeObject(sessionDataJson, SbmSessionDataJsonSerializerSettings); + } + + //------------------------------------------------------------------- + } + //====================================================================== +} diff --git a/SaBusinessMgrCoreWebControl/Control/Models/Model_Home.cs b/SaBusinessMgrCoreWebControl/Control/Models/Model_Home.cs new file mode 100644 index 0000000..d014a81 --- /dev/null +++ b/SaBusinessMgrCoreWebControl/Control/Models/Model_Home.cs @@ -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) + { + } + + //------------------------------------------------------------------- + } + //======================================================================= +} diff --git a/SaBusinessMgrCoreWebControl/Control/Models/SbmModel_Control.cs b/SaBusinessMgrCoreWebControl/Control/Models/SbmModel_Control.cs new file mode 100644 index 0000000..9b25b19 --- /dev/null +++ b/SaBusinessMgrCoreWebControl/Control/Models/SbmModel_Control.cs @@ -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) + { + } + + //------------------------------------------------------------------- + } + //======================================================================= +} diff --git a/SaBusinessMgrCoreWebControl/SaBusinessMgrCoreWebControl.csproj b/SaBusinessMgrCoreWebControl/SaBusinessMgrCoreWebControl.csproj index 43e59e7..cad6cf7 100644 --- a/SaBusinessMgrCoreWebControl/SaBusinessMgrCoreWebControl.csproj +++ b/SaBusinessMgrCoreWebControl/SaBusinessMgrCoreWebControl.csproj @@ -1,52 +1,52 @@ - - netcoreapp3.1 - false - Sarand.BusinessMgr.Net.Core.Web - + + net6.0 + false + Sarand.BusinessMgr.Net.Core.Web + - - - - + + + + - - - - - - - - - - + + + + + + + + + + - - - + + + - - - PreserveNewest - true - PreserveNewest - - - PreserveNewest - true - PreserveNewest - - - PreserveNewest - true - PreserveNewest - - - PreserveNewest - true - PreserveNewest - - + + + PreserveNewest + true + PreserveNewest + + + PreserveNewest + true + PreserveNewest + + + PreserveNewest + true + PreserveNewest + + + PreserveNewest + true + PreserveNewest + + diff --git a/SaBusinessMgrCoreWebControl/Views/Shared/_Layout.cshtml b/SaBusinessMgrCoreWebControl/Views/Shared/_Layout.cshtml index bfbd97d..974182b 100644 --- a/SaBusinessMgrCoreWebControl/Views/Shared/_Layout.cshtml +++ b/SaBusinessMgrCoreWebControl/Views/Shared/_Layout.cshtml @@ -1,4 +1,12 @@ - +@model SbmModel_Control +@inject Microsoft.AspNetCore.Antiforgery.IAntiforgery AntiForgery; +@inject SbmLocaliser Localiser +@* + Application: BusinessMgr Web Control + Purpose: Page template. +*@ + + @@ -34,7 +42,7 @@
- © 2021 - Sarand BusinessMgr + @(Model.CopyrightNotice) - Sarand BusinessMgr
diff --git a/SaBusinessMgrCoreWebControl/Views/_ViewImports.cshtml b/SaBusinessMgrCoreWebControl/Views/_ViewImports.cshtml index a0093db..6b7bbad 100644 --- a/SaBusinessMgrCoreWebControl/Views/_ViewImports.cshtml +++ b/SaBusinessMgrCoreWebControl/Views/_ViewImports.cshtml @@ -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 diff --git a/SaBusinessMgrCoreWebControl/appsettings.Machine.Sarand107.json b/SaBusinessMgrCoreWebControl/appsettings.Machine.Sarand107.json deleted file mode 100644 index 4b9cf40..0000000 --- a/SaBusinessMgrCoreWebControl/appsettings.Machine.Sarand107.json +++ /dev/null @@ -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" - } - } - } -} diff --git a/SaBusinessMgrCoreWebControl/appsettings.Machine.Sarand110.json b/SaBusinessMgrCoreWebControl/appsettings.Machine.Sarand110.json new file mode 100644 index 0000000..ef57f64 --- /dev/null +++ b/SaBusinessMgrCoreWebControl/appsettings.Machine.Sarand110.json @@ -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" + } + } + } +} diff --git a/SaBusinessMgrCoreWebPortal/Localisation/SbmPortalProperties.resx b/SaBusinessMgrCoreWebPortal/Localisation/SbmPortalProperties.resx index 34d88b8..79958de 100644 --- a/SaBusinessMgrCoreWebPortal/Localisation/SbmPortalProperties.resx +++ b/SaBusinessMgrCoreWebPortal/Localisation/SbmPortalProperties.resx @@ -117,6 +117,18 @@ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + Association + + + Create Association + + + Update Association + + + Associations + Commodities @@ -156,6 +168,9 @@ Any + + Association + Associations @@ -189,6 +204,9 @@ Contacts + + Context + Create @@ -198,6 +216,9 @@ Create Organisation + + Customers + Description @@ -279,6 +300,9 @@ Sign-out + + Suppliers + Type diff --git a/SaBusinessMgrCoreWebPortal/Portal/App/SbmApplication_Portal.cs b/SaBusinessMgrCoreWebPortal/Portal/App/SbmApplication_Portal.cs index 5e8f1d2..4ec46d9 100644 --- a/SaBusinessMgrCoreWebPortal/Portal/App/SbmApplication_Portal.cs +++ b/SaBusinessMgrCoreWebPortal/Portal/App/SbmApplication_Portal.cs @@ -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 appConfigs = new Dictionary(); - private Dictionary emailConfigs = new Dictionary(); - // 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 sessionContexts = new Dictionary(); + //======================================================================= + public class SbmApplication_Portal : SbmApplication_Web + { + //------------------------------------------------------------------- + // Data members. + private PropertySet properties; + private Dictionary appConfigs = new Dictionary(); + private Dictionary emailConfigs = new Dictionary(); + 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 DbIdentifiers { get; } = new List(); + public ISacsDatabase BusMgrDatabase { get; set; } + } + private readonly Dictionary sessionContexts = new Dictionary(); - //------------------------------------------------------------------- - // 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 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 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 GetDbIdentifiers(SbmSessionContext sessionContext) + { + return sessionContexts.GetValue(sessionContext.Index, null)?.DbIdentifiers ?? new List(); + } + 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. - /// Initialise environment - from Startup. - internal void InitWebHostEnvironment(IWebHostEnvironment env) - { - InitEnvironment(env); - } - /// Initialise environment - from internal (above) or unit tests. - 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. + /// Initialise environment - from Startup. + internal void InitWebHostEnvironment(IWebHostEnvironment env) + { + InitEnvironment(env); + } + /// Initialise environment - from internal (above) or unit tests. + 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)); + } + } - //------------------------------------------------------------------- - } - //======================================================================= + //------------------------------------------------------------------- + } + //======================================================================= } diff --git a/SaBusinessMgrCoreWebPortal/Portal/App/SbmSessionData_Portal.cs b/SaBusinessMgrCoreWebPortal/Portal/App/SbmSessionData_Portal.cs index a712dd8..a940f99 100644 --- a/SaBusinessMgrCoreWebPortal/Portal/App/SbmSessionData_Portal.cs +++ b/SaBusinessMgrCoreWebPortal/Portal/App/SbmSessionData_Portal.cs @@ -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) - { } + {} /// /// SessionData construction. /// SessionContext and UserAccount must be supplied. /// Organisation is optional. /// - public SbmSessionData_Portal(SbmSessionContext sessionContext, SaSecurityItem securityItemUser, SbmAffiliate affiliateUser, List supervisedOrganisations, SbmAssociation associationOrganisation, SbmAffiliate affiliateOrganisation) : + public SbmSessionData_Portal(SbmSessionContext sessionContext, String dbIdentifier, SaSecurityItem securityItemUser, SbmAffiliate affiliateUser, List 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. + + /// Database context specified on sign-in. + public String DbIdentifier { get; set; } + //------------------------------------------------------------------- /// /// User Profile. @@ -98,11 +106,22 @@ namespace Sarand.BusinessMgr.Net.Core.Web.Portal.App } public FilterData Filter { get; } = new FilterData(); } - /// - /// Contacts filter properties. - /// + /// Contacts filter properties. 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(); + } + /// Associations filter properties. + 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(); } - /// - /// Commodities filter properties. - /// + /// Commodities filter properties. public CommoditiesData Commodities { get; } = new CommoditiesData(); } public UserProfileData Profile { get; protected set; } = new UserProfileData(); diff --git a/SaBusinessMgrCoreWebPortal/Portal/Controllers/AssociationController-Ajax.cs b/SaBusinessMgrCoreWebPortal/Portal/Controllers/AssociationController-Ajax.cs new file mode 100644 index 0000000..aebf310 --- /dev/null +++ b/SaBusinessMgrCoreWebPortal/Portal/Controllers/AssociationController-Ajax.cs @@ -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 Associations { get; } = new List(); + // Reference data. + public List Statuses { get; } = new List(); + 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 columns = new List() + { + 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); + } + + //------------------------------------------------------------------- + } + //======================================================================= +} diff --git a/SaBusinessMgrCoreWebPortal/Portal/Controllers/AssociationController.cs b/SaBusinessMgrCoreWebPortal/Portal/Controllers/AssociationController.cs new file mode 100644 index 0000000..5a03059 --- /dev/null +++ b/SaBusinessMgrCoreWebPortal/Portal/Controllers/AssociationController.cs @@ -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 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); + } + + //------------------------------------------------------------------- + } + //======================================================================= +} diff --git a/SaBusinessMgrCoreWebPortal/Portal/Controllers/CommodityController-Ajax.cs b/SaBusinessMgrCoreWebPortal/Portal/Controllers/CommodityController-Ajax.cs index 68cf21a..bf243ce 100644 --- a/SaBusinessMgrCoreWebPortal/Portal/Controllers/CommodityController-Ajax.cs +++ b/SaBusinessMgrCoreWebPortal/Portal/Controllers/CommodityController-Ajax.cs @@ -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; } diff --git a/SaBusinessMgrCoreWebPortal/Portal/Controllers/ContactController-Ajax.cs b/SaBusinessMgrCoreWebPortal/Portal/Controllers/ContactController-Ajax.cs index 83bd3af..b3655ea 100644 --- a/SaBusinessMgrCoreWebPortal/Portal/Controllers/ContactController-Ajax.cs +++ b/SaBusinessMgrCoreWebPortal/Portal/Controllers/ContactController-Ajax.cs @@ -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; } diff --git a/SaBusinessMgrCoreWebPortal/Portal/Controllers/HomeController.cs b/SaBusinessMgrCoreWebPortal/Portal/Controllers/HomeController.cs index 1534673..72e2411 100644 --- a/SaBusinessMgrCoreWebPortal/Portal/Controllers/HomeController.cs +++ b/SaBusinessMgrCoreWebPortal/Portal/Controllers/HomeController.cs @@ -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 logger) : - base(localiser, logger) - { - } + //------------------------------------------------------------------- + // Construction. + public HomeController(SbmLocaliser localiser, ILogger 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 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 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 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 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 }); + } - //------------------------------------------------------------------- - } - //======================================================================= + //------------------------------------------------------------------- + } + //======================================================================= } diff --git a/SaBusinessMgrCoreWebPortal/Portal/Controllers/SbmController_Portal.cs b/SaBusinessMgrCoreWebPortal/Portal/Controllers/SbmController_Portal.cs index a60a355..49f633e 100644 --- a/SaBusinessMgrCoreWebPortal/Portal/Controllers/SbmController_Portal.cs +++ b/SaBusinessMgrCoreWebPortal/Portal/Controllers/SbmController_Portal.cs @@ -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 /// - Open user session - store details against Sesion Data. /// - Set 'Keep logged in' cookie if required. /// - protected bool SignUserIn(ISbmUserContext userContext, SaSecurityItem securityItemUser, SbmAffiliate affiliateUser, List supervisedOrganisations, SbmAssociation associationOrganisation, SbmAffiliate affiliateOrganisation, bool keepLoggedIn, ref String errorMessage) + protected bool SignUserIn(ISbmUserContext userContext, String dbIdentifier, SaSecurityItem securityItemUser, SbmAffiliate affiliateUser, List 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 //------------------------------------------------------------------- /// Start a new user session on sign-in. - protected bool OpenUserSession(SbmSessionContext sessionContext, SaSecurityItem securityItemUser, SbmAffiliate affiliateUser, List supervisedOrganisations, SbmAssociation associationOrganisation, SbmAffiliate affiliateOrganisation) + protected bool OpenUserSession(SbmSessionContext sessionContext, String dbIdentifier, SaSecurityItem securityItemUser, SbmAffiliate affiliateUser, List 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 GetValidParams() { List values = new List(); - 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(); diff --git a/SaBusinessMgrCoreWebPortal/Portal/Controllers/SecurityController-Ajax.cs b/SaBusinessMgrCoreWebPortal/Portal/Controllers/SecurityController-Ajax.cs index de18668..a1c1b8b 100644 --- a/SaBusinessMgrCoreWebPortal/Portal/Controllers/SecurityController-Ajax.cs +++ b/SaBusinessMgrCoreWebPortal/Portal/Controllers/SecurityController-Ajax.cs @@ -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 sessionContextIdxs = SbmSessionContextHelper_Web.GetSessionContextIdxsForUsersBusMgrDatabase(model.EmailAddress); - foreach (ushort sessionContextIdx in sessionContextIdxs) + using (DisposableCollection disposableItems = new DisposableCollection()) { - SbmSessionContext sessionContext = SbmSessionContextHelper_Web.GetSessionContext(sessionContextIdx); - SbmUserContext_Web userContext = new SbmUserContext_Web(sessionContext); - using (DisposableCollection disposableItems = new DisposableCollection()) + // Identify session contexts with a BusMgr database that hosts the specified email address. + List 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 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 sessionContextIdxs = SbmSessionContextHelper_Web.GetSessionContextIdxsForUsersBusMgrDatabase(requestPasswordParams.EmailAddress); + // Identify session contexts for databases with specified Id that hosts the specified email address. + List 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 sessionContextIdxs = SbmSessionContextHelper_Web.GetSessionContextIdxsForUsersBusMgrDatabase(model.EmailAddress); + List 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+!^]", 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 GetValidParams() { List values = new List(); - 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))); diff --git a/SaBusinessMgrCoreWebPortal/Portal/Controllers/SecurityController.cs b/SaBusinessMgrCoreWebPortal/Portal/Controllers/SecurityController.cs index 2e3280b..7a35825 100644 --- a/SaBusinessMgrCoreWebPortal/Portal/Controllers/SecurityController.cs +++ b/SaBusinessMgrCoreWebPortal/Portal/Controllers/SecurityController.cs @@ -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 sessionContextIdxs = SbmSessionContextHelper_Web.GetSessionContextIdxsForUsersBusMgrDatabase(requestPasswordParams.EmailAddress); + List sessionContextIdxs = SbmSessionContextHelper_Web.GetSessionContextIdxsForContextUserEmail(requestPasswordParams.DbIdentifier, requestPasswordParams.EmailAddress); foreach (ushort sessionContextIdx in sessionContextIdxs) { // Consoles and Managers must be associated with the specific context. diff --git a/SaBusinessMgrCoreWebPortal/Portal/Models/Model_Association.cs b/SaBusinessMgrCoreWebPortal/Portal/Models/Model_Association.cs new file mode 100644 index 0000000..d1493fc --- /dev/null +++ b/SaBusinessMgrCoreWebPortal/Portal/Models/Model_Association.cs @@ -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(); + } + + //------------------------------------------------------------------- + } + //======================================================================= +} diff --git a/SaBusinessMgrCoreWebPortal/Portal/Models/Model_Security.cs b/SaBusinessMgrCoreWebPortal/Portal/Models/Model_Security.cs index af18244..f06a6b1 100644 --- a/SaBusinessMgrCoreWebPortal/Portal/Models/Model_Security.cs +++ b/SaBusinessMgrCoreWebPortal/Portal/Models/Model_Security.cs @@ -30,6 +30,8 @@ namespace Sarand.BusinessMgr.Net.Core.Web.Portal.Models //------------------------------------------------------------------- // Form properties. + /// Database config identifier. + 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(); } diff --git a/SaBusinessMgrCoreWebPortal/SaBusinessMgrCoreWebPortal.csproj b/SaBusinessMgrCoreWebPortal/SaBusinessMgrCoreWebPortal.csproj index 0b2f235..34bf090 100644 --- a/SaBusinessMgrCoreWebPortal/SaBusinessMgrCoreWebPortal.csproj +++ b/SaBusinessMgrCoreWebPortal/SaBusinessMgrCoreWebPortal.csproj @@ -1,44 +1,43 @@ - - netcoreapp3.1 - false - Sarand.BusinessMgr.Net.Core.Web - Sarand.BusinessMgr.Net.Core.Web - SaBusinessMgrCoreWebPortal - SaBusinessMgrCoreWebPortal - Sarand - BusinessMgr - 3.0.0 - + + net6.0 + false + Sarand.BusinessMgr.Net.Core.Web + Sarand.BusinessMgr.Net.Core.Web + SaBusinessMgrCoreWebPortal + SaBusinessMgrCoreWebPortal + Sarand + BusinessMgr + 3.0.0 + - - - - + + + + - - - - - - - - - - + + + + + + + + + + - - - PreserveNewest - true - PreserveNewest - - + + + PreserveNewest + true + PreserveNewest + + - - - - + + + diff --git a/SaBusinessMgrCoreWebPortal/Views/Association/Index.cshtml b/SaBusinessMgrCoreWebPortal/Views/Association/Index.cshtml new file mode 100644 index 0000000..61c4732 --- /dev/null +++ b/SaBusinessMgrCoreWebPortal/Views/Association/Index.cshtml @@ -0,0 +1,135 @@ +@model Model_Association +@inject SbmLocaliser Localiser +@{ + /* Associations - Index */ + ViewData["Title"] = "Associations"; +} + +
+ + + +
+ +
+ +
+ + @* Filter Panel *@ + @using (@Html.BeginForm(FormMethod.Post, new { @id = "frmAssociationFilter", @class = "sbm-form", @onSubmit = "return false;" })) + { +
+ @* Header - Page caption, paging controls, actions. *@ +
+
+
@Localiser["association.caption.associations"]
+
+
+ +
+
+ @* Burger menu *@ + +
+
+
+
+
+ @* Association filter - name etc. *@ +
+
@Localiser["label.search"]
+
+ @Html.TextBoxFor(model => model.Filter.AssociationSearch, new { @id = "", @class = "FilterAssociation", @maxlength = "32" }) +
+
+ @* Association Category *@ +
+
@Localiser["label.type"]
+
+ @Html.DropDownListFor(model => model.Filter.AssociationType, Model.GetAssociationTypeCodesForSelect(), Localiser["label.any"], new { @id = "", @class = "FilterAssociationType sbm-select" }) +
+
+
+ + @* Filter element templates *@ +
+ @* Burger dropdown item template - ul is ignored *@ +
    +
  • XXX
  • +
+
+ +
+ } + +
+ + @* Separate filter from results when alondside each other. *@ +
+ +
+ +
+ + @* Results Panel *@ +
+
+
+ + @* Element templates *@ +
+ + @* Association item template *@ +
+
+
+ @* Association caption *@ +
-
+
+
+ @* Burger menu *@ + +
+
+
+
+
+
@Localiser["label.associations"]: 
+
+
+
+
+
+
+
+
+
+
+ + @* Burger dropdown item template - ul is ignored *@ +
    +
  • XXX
  • +
+ +
+
+ +
+
+
+ + diff --git a/SaBusinessMgrCoreWebPortal/Views/Association/Panel-AssociationDialog.cshtml b/SaBusinessMgrCoreWebPortal/Views/Association/Panel-AssociationDialog.cshtml new file mode 100644 index 0000000..58c95de --- /dev/null +++ b/SaBusinessMgrCoreWebPortal/Views/Association/Panel-AssociationDialog.cshtml @@ -0,0 +1,81 @@ +@model Model_Association +@inject SbmLocaliser Localiser +@{ + /* Associations - Association dialog panel */ +} + + + +@using (@Html.BeginForm(FormMethod.Post, new { @id = "frmAssociationId", @class = "sbm-form" })) +{ + @Html.HiddenFor(model => model.AssociationId, new { @class = "AssociationId" }) +} + +
+ + @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 *@ +
+
+ +
+
+ @Html.DropDownListFor(model => model.AssociationType, Model.GetAssociationTypeCodesForSelect(), Localiser["label.select"], new { @id = "cbAssociationType", @class = "AssociationType sbm-select" }) +
+
+
+ + @* Association Code *@ +
+
+ +
+
+ @Html.TextBoxFor(model => model.AssociationCode, new { @id = "ifAssociationCode", @class = "AssociationCode sbm-input", @maxlength = @Model.GetControlMaxLength(SbmDbDfn.ColumnIdx.Association_Code) }) +
+
+
+ + @* Association Description *@ +
+
+ +
+
+ @Html.TextBoxFor(model => model.AssociationDescDetail, new { @id = "ifAssociationDescDetail", @class = "AssociationDescDetail sbm-input", @maxlength = @Model.GetControlMaxLength(SbmDbDfn.ColumnIdx.Association_DescDetail) }) +
+
+
+ + @* Association status *@ +
+
+ +
+
+ +
+
+
+ + @* Association Notes *@ +
+
+ +
+
+ @Html.TextAreaFor(model => model.AssociationNotes, new { @id = "ifAssociationNotes", @class = "AssociationNotes sbm-textarea", @rows = "3", @maxlength = @Model.GetControlMaxLength(SbmDbDfn.ColumnIdx.Association_Notes) }) +
+
+
+ + } + +
diff --git a/SaBusinessMgrCoreWebPortal/Views/Commodity/Index.cshtml b/SaBusinessMgrCoreWebPortal/Views/Commodity/Index.cshtml index c33be72..fd0f278 100644 --- a/SaBusinessMgrCoreWebPortal/Views/Commodity/Index.cshtml +++ b/SaBusinessMgrCoreWebPortal/Views/Commodity/Index.cshtml @@ -85,8 +85,9 @@
- @* element templates *@ + @* Element templates *@
+ @* Commodity item template *@
@@ -118,10 +119,12 @@
+ @* Burger dropdown item template - ul is ignored *@
  • XXX
+ diff --git a/SaBusinessMgrCoreWebPortal/Views/Security/RequestPassword.cshtml b/SaBusinessMgrCoreWebPortal/Views/Security/RequestPassword.cshtml index 9424b30..6590f63 100644 --- a/SaBusinessMgrCoreWebPortal/Views/Security/RequestPassword.cshtml +++ b/SaBusinessMgrCoreWebPortal/Views/Security/RequestPassword.cshtml @@ -14,6 +14,17 @@ @using (@Html.BeginForm(FormMethod.Post, new { @id = "frmRequestPassword", @class = "sbm-form" })) { +
+
+
+ +
+
+ @Html.TextBoxFor(model => model.DbIdentifier, new { @id = "ifDbIdentifier", @class = "sbm-input", @maxlength = @Sbm.Config.Database.Identifier.MaxLength }) +
+
+
+
diff --git a/SaBusinessMgrCoreWebPortal/Views/Security/Signin.cshtml b/SaBusinessMgrCoreWebPortal/Views/Security/Signin.cshtml index a9bc483..8b43858 100644 --- a/SaBusinessMgrCoreWebPortal/Views/Security/Signin.cshtml +++ b/SaBusinessMgrCoreWebPortal/Views/Security/Signin.cshtml @@ -3,7 +3,6 @@ @inject SbmLocaliser Localiser @{ ViewData["Title"] = "Signin"; - ulong emailAddressLen = SaSecurityDbDfn.GetColTextLen(SaSecurityDbDfn.ColumnIdx.SaSecItem_EmailAddress); }
@@ -12,23 +11,34 @@
@Localiser["label.signin"]
-
+
@using (@Html.BeginForm(FormMethod.Post, new { @id = "frmSignIn", @class = "sbm-form" })) {
+
+
+ +
+
+ @Html.TextBoxFor(model => model.DbIdentifier, new { @id = "ifDbIdentifier", @class = "sbm-input", @maxlength = @Sbm.Config.Database.Identifier.MaxLength }) +
+
+
+ +
- @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) })
-
+
@@ -43,7 +53,7 @@
-
+
diff --git a/SaBusinessMgrCoreWebPortal/Views/Shared/_Layout.cshtml b/SaBusinessMgrCoreWebPortal/Views/Shared/_Layout.cshtml index cd6cb11..5c73036 100644 --- a/SaBusinessMgrCoreWebPortal/Views/Shared/_Layout.cshtml +++ b/SaBusinessMgrCoreWebPortal/Views/Shared/_Layout.cshtml @@ -122,6 +122,7 @@