v1.3.0 Restructure Saxonite workspace and upgrade to .NET 10 (#1)

Move Saxonite’s projects into a dedicated `Workspace` folder and align the solution with .NET 10 and the current Common workspace. Update encryption integration, test project structure and coverage, and repository guidance and upgrade notes.

- Relocate application and library projects while retaining Git rename history.
- Split tests into Admin, Online, and Shared projects; add representative behavioral and serialization tests.
- Update Common integration, application version, and repository documentation.

Application version: 1.3.0

Reviewed-on: #1
Co-authored-by: andy <andy@sarand.com>
This commit was merged in pull request #1.
This commit is contained in:
2026-10-16 18:24:16 +01:00
committed by andy
parent 2c3fb146ab
commit 9f22b522df
1996 changed files with 1992 additions and 1179 deletions
@@ -0,0 +1,281 @@
using Microsoft.AspNetCore.Mvc;
using Sarand.Net.Standard.Base.App;
using Sarand.Net.Standard.Base.Bus;
using Sarand.Net.Standard.Base.Core;
using Sarand.Net.Standard.Base.Dbas;
using Sarand.Net.Standard.Base.Utils;
using Saxonite.Net.Core.Web.Admin.Models;
using Saxonite.Net.Standard.Base.App;
using Saxonite.Net.Standard.Base.Bus;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace Saxonite.Net.Core.Web.Admin.Controllers
{
//=======================================================================
partial class RegionController
{
//-------------------------------------------------------------------
// Region data.
private class RegionListData : AjaxResponse_Data.ItemData_Base
{
public int? RegionType { get; set; } = null;
public String RegionTypeDesc { get; set; }
public String CountryIso2Code { get; set; }
public String Name { get; set; }
public String ParentName { get; set; }
}
private class RegionItemData : RegionListData
{
public String RegionTypeCode { get; set; }
public String RegionIdParent { get; set; }
}
private void PopulateRegionResponseListData(SaxRegion region, RegionListData data)
{
data.Id = SysId.ToString(region.Id);
data.RegionType = (int)region.RegionType;
data.RegionTypeDesc = RegionTypeCodes.GetDescForType(region.RegionType);
data.CountryIso2Code = region.CountryIso2Code;
data.Name = region.Name;
using (SaxRegion parentRegion = ReferenceMgr.GetRegionById(region.RegionIdParent))
{
if (parentRegion != null) data.ParentName = parentRegion.Name;
}
data.StatusCode = region.StatusCode;
data.StatusDesc = region.StatusDesc;
}
private void PopulateRegionResponseItemData(SaxRegion region, RegionItemData data)
{
PopulateRegionResponseListData(region, data);
data.RegionTypeCode = RegionTypeCodes.GetCodeForType(region.RegionType);
data.RegionIdParent = SysId.ToString(region.RegionIdParent);
}
//-------------------------------------------------------------------
// Get Region List ajax request.
//-------------------------------------------------------------------
private class AjaxResponse_RegionList : AjaxResponse_Data
{
// Construction.
public AjaxResponse_RegionList(SaxModel_Admin model) : base(model) { }
// Region list.
public List<RegionListData> Regions { get; } = new List<RegionListData>();
// Reference data.
public List<CodeDesc> Statuses { get; } = new List<CodeDesc>();
public String DefaultStatusCode { get; set; }
}
//-------------------------------------------------------------------
[HttpPost]
[Route("Region/Ajax/GetList")]
public IActionResult Ajax_GetList(Model_Region model)
{
String fn = GetType().Name + ".Ajax_GetList";
String messageTitle = Localiser["region.caption.regions"];
AjaxResponse_RegionList responseData = new AjaxResponse_RegionList(model);
// Validate session.
if (!ValidUserSession)
return GetAjaxResult_InvalidSession(responseData);
// Reference data - statuses.
foreach (SaStatus status in SaxRegion.ValidStatuses.FindAll(item => item != SaStatus.Deleted))
responseData.Statuses.Add(SacsSystem.GetStatusCode(status), Localiser.GetStatusCaption(status));
responseData.DefaultStatusCode = SacsSystem.GetStatusCode(SaxRegion.DefaultStatus);
// Item filter.
SaxRegion.Params prmRegion = new SaxRegion.Params();
prmRegion.RegionFilter = model.Filter.Region;
if (model.Filter.RegionTypeCode != null)
prmRegion.RegionType = model.FilterRegionType;
prmRegion.StatusCat = SaxRegion.StatusCat.NotDeleted;
model.Paging.ItemTotal = (ulong)ReferenceMgr.GetRegionCount(prmRegion);
model.PopulatePagingTo(prmRegion.ResultsPaging);
// Item columns.
ColumnDfn colId = ReferenceMgr.GetRegionColumnDfn(SaxRegion.Column.Id);
ColumnDfn colType = ReferenceMgr.GetRegionColumnDfn(SaxRegion.Column.RegionType);
ColumnDfn colIso2Code = ReferenceMgr.GetRegionColumnDfn(SaxRegion.Column.CountryIso2Code);
ColumnDfn colName = ReferenceMgr.GetRegionColumnDfn(SaxRegion.Column.Name);
ColumnDfn colParentName = ReferenceMgr.GetRegionColumnDfn(SaxRegion.Column.Parent_Name);
ColumnDfn colStatus = ReferenceMgr.GetRegionColumnDfn(SaxRegion.Column.Status);
List<ColumnDfn> columns = new List<ColumnDfn>()
{
colId,
colType,
colIso2Code,
colName,
colParentName,
colStatus
};
// Sort order.
prmRegion.SortColumns.Add(colName);
// Query items.
using (DbResultSet resultSet = ReferenceMgr.ExecuteRegionQuery(columns, prmRegion))
{
while (resultSet != null && resultSet.HasRows && resultSet.NextRecord())
{
NamedValue.Map values = resultSet.GetRecordValues();
Sax.RegionType regionType = (Sax.RegionType)values.GetLong(colType.DataName);
RegionListData regionData = new RegionListData()
{
Id = values.GetString(colId.DataName),
RegionType = (int)regionType,
RegionTypeDesc = RegionTypeCodes.GetDescForType(regionType),
CountryIso2Code = values.GetString(colIso2Code.DataName),
Name = values.GetString(colName.DataName),
ParentName = values.GetString(colParentName.DataName),
StatusCode = values.GetString(colStatus.DataName),
StatusDesc = SacsSystem.GetStatusDescForCode(values.GetString(colStatus.DataName))
};
responseData.Regions.Add(regionData);
}
}
if (model.Paging.IsPaged)
model.Paging.ItemCount = (ushort)responseData.Regions.Count;
// Complete response.
responseData.SetPaging(model);
responseData.Success = true;
return Json(responseData);
}
//-------------------------------------------------------------------
// Get Region ajax request.
//-------------------------------------------------------------------
private class AjaxResponse_Region : AjaxResponse_Data
{
// Construction.
public AjaxResponse_Region(SaxModel_Admin model) : base(model) { }
// Region details.
public RegionItemData region { get; } = new RegionItemData();
}
//-------------------------------------------------------------------
[HttpPost]
[Route("Region/Ajax/GetItem")]
public IActionResult Ajax_GetItem(Model_Region model)
{
String fn = GetType().Name + ".Ajax_GetItem";
String messageTitle = Localiser["region.caption.region"];
AjaxResponse_Region responseData = new AjaxResponse_Region(model);
// Validate session.
if (!ValidUserSession)
return GetAjaxResult_InvalidSession(responseData);
// Record disposable items as they are created so all can be disposed when needed.
using (DisposableCollection<IDisposable> disposableItems = new DisposableCollection<IDisposable>())
{
// Identify item.
SysId regionId = SysId.ToSysId(model.RegionId);
if (!SysId.IsValid(regionId))
return Json(responseData.SetPopupMessage(messageTitle, Localiser["msg.error.item.id.not.specified"]));
// Retrieve item.
SaxRegion region = ReferenceMgr.GetRegionById(regionId);
disposableItems.Add(region);
if (region == null)
{
SaLogger.Warn(fn + " Failed to retrieve region for Id: '" + model.RegionId + "'");
return Json(responseData.SetPopupMessage(messageTitle, Localiser["msg.error.item.id.not.found"]));
}
// Construct response.
PopulateRegionResponseItemData(region, responseData.region);
}
// Complete response.
responseData.Success = true;
return Json(responseData);
}
//-------------------------------------------------------------------
// Store Region ajax request.
//-------------------------------------------------------------------
[HttpPost]
[Route("Region/Ajax/StoreItem")]
public IActionResult Ajax_StoreItem(Model_Region model)
{
String fn = GetType().Name + ".Ajax_StoreItem";
String messageTitle = Localiser["region.caption.region"];
AjaxResponse_Region responseData = new AjaxResponse_Region(model);
// Validate session.
if (!ValidUserSession)
return GetAjaxResult_InvalidSession(responseData);
// Record disposable items as they are created so all can be disposed when needed.
using (DisposableCollection<IDisposable> disposableItems = new DisposableCollection<IDisposable>())
{
// Validate item properties.
model.ValidateRegionData(responseData);
if (responseData.HasControlErrors())
return Json(responseData);
// Retrieve existing item.
SysId regionId = SysId.ToSysId(model.RegionId);
SaxRegion region = ReferenceMgr.GetRegionById(regionId);
disposableItems.Add(region);
if (region == null && SysId.IsValid(regionId))
{
SaLogger.Warn(fn + " Failed to retrieve region for Id: '" + model.RegionId + "'");
return Json(responseData.SetPopupMessage(messageTitle, Localiser["msg.error.item.id.not.found"]));
}
// Create new item if required.
if (region == null)
{
region = ReferenceMgr.CreateRegion();
disposableItems.Add(region);
}
// Populate item properties.
if (model.RegionTypeCode != null)
region.RegionType = model.RegionType;
if (model.RegionCountryIso2Code != null)
region.CountryIso2Code = model.RegionCountryIso2Code;
if (model.RegionName != null)
region.Name = model.RegionName;
if (model.RegionIdParent != null)
region.RegionIdParent = SysId.ToSysId(model.RegionIdParent);
if (model.RegionStatusCode != null)
region.Status = model.RegionStatus;
// Store item.
if (!SysId.IsValid(region.Id))
{
if (!ReferenceMgr.InsertRegion(region, DbUpdateInfo))
{
SaLogger.Warn(fn + " Failed to insert region: " + ReferenceMgr.ErrorStateMessage);
return Json(responseData.SetPopupMessage(messageTitle, Localiser["msg.error.item.insert.failed"] + "\n\n" + ReferenceMgr.ErrorStateMessage));
}
}
else
{
if (!ReferenceMgr.UpdateRegion(region, DbUpdateInfo))
{
SaLogger.Warn(fn + " Failed to update region [Id:" + SysId.ToString(region.Id) + "]: " + ReferenceMgr.ErrorStateMessage);
return Json(responseData.SetPopupMessage(messageTitle, Localiser["msg.error.item.update.failed"] + "\n\n" + ReferenceMgr.ErrorStateMessage));
}
}
// Construct response.
PopulateRegionResponseItemData(region, responseData.region);
}
// Complete response.
responseData.Success = true;
return Json(responseData);
}
//-------------------------------------------------------------------
}
//=======================================================================
}