Files
Saxonite/Workspace/SaxoniteCoreWebAdmin/Admin/Controllers/OrganisationController-Ajax-Associations.cs
andy 9f22b522df 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>
2026-10-16 18:24:16 +01:00

657 lines
32 KiB
C#

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.Diagnostics;
using System.Linq;
using System.Threading.Tasks;
namespace Saxonite.Net.Core.Web.Admin.Controllers
{
//=======================================================================
// Controller: Organisation - Ajax - Associations
//=======================================================================
partial class OrganisationController
{
//-------------------------------------------------------------------
// Association type combinations.
//-------------------------------------------------------------------
private static readonly String optionLabel_AffiliatedTo = "Affiliated to";
private static readonly String optionLabel_Affiliate = "Affiliation for";
private static readonly String optionLabel_Partner = "Partner";
// Associations are between two items (From & To) but in the context of
// displaying associations for an organisation, the organisation may be
// the 'From' item (normal) or the 'To' item (reversed).
private class AssociationTypeData
{
public AssociationTypeData()
{
// No parameter construction - populating association details for individual item (see Populate()).
}
public AssociationTypeData(Sax.AssociationTypeOption associationTypeOption)
{
// Population on construction - generic association type definitions, eg. for drop down menu options.
Populate(associationTypeOption);
}
public void Populate(Sax.AssociationTypeOption associationTypeOption)
{
// Default labels - option to select association etc.
Type = ((int)associationTypeOption).ToString();
Label = GetOptionLabel(associationTypeOption);
}
public void Populate(Sax.AssociationType associationType, bool reversed)
{
// Default labels - option to select association etc.
Populate(GetAssociationTypeOption(associationType, reversed));
}
public void Populate(Sax.AssociationType associationType, bool reversed, String label)
{
// Populate for specific labels, eg. to identify nature of associated organisation.
Sax.AssociationTypeOption associationTypeOption = GetAssociationTypeOption(associationType, reversed);
Type = ((int)associationTypeOption).ToString();
Label = label;
}
// Properties.
public String Type { get; set; }
public String Label { get; set; }
// Utilities.
public static Sax.AssociationTypeOption GetAssociationTypeOption(Sax.AssociationType associationType, bool reversed)
{
switch (associationType)
{
case Sax.AssociationType.Affiliate: return reversed ? Sax.AssociationTypeOption.AffiliateChild : Sax.AssociationTypeOption.AffiliateParent;
case Sax.AssociationType.Partner: return Sax.AssociationTypeOption.Partner;
}
return Sax.AssociationTypeOption.Unknown;
}
public static String GetOptionLabel(Sax.AssociationTypeOption associationTypeOption)
{
switch (associationTypeOption)
{
case Sax.AssociationTypeOption.AffiliateParent: return optionLabel_AffiliatedTo;
case Sax.AssociationTypeOption.AffiliateChild: return optionLabel_Affiliate;
case Sax.AssociationTypeOption.Partner: return optionLabel_Partner;
}
return "";
}
}
//-------------------------------------------------------------------
// Association data.
//-------------------------------------------------------------------
private class AssociationListData : AjaxResponse_Data.ItemData_Base
{
public class OrganisationData
{
public String Id { get; set; }
public String Name { get; set; }
public String Sport { get; set; }
public String Region { get; set; }
}
public OrganisationData Organisation { get; } = new OrganisationData();
public OrganisationData OrganisationAssoc { get; } = new OrganisationData();
public AssociationTypeData Assoc { get; } = new AssociationTypeData();
}
private class AssociationItemData : AssociationListData
{
}
private void PopulateAssociationResponseListData(SaxModel_Admin model, AssociationListData data, SaxItemAssociation itemAssociation, SaxOrganisation organisation, SaxOrganisation organisationAssoc)
{
data.Id = SysId.ToString(itemAssociation.Id);
// Organisation.
data.Organisation.Id = SysId.ToString(organisation.Id);
data.Organisation.Name = organisation.Name;
// Associated Organisation.
data.OrganisationAssoc.Id = SysId.ToString(organisationAssoc.Id);
data.OrganisationAssoc.Name = organisationAssoc.Name;
if (!organisation.SportIdPrimary.Equals(organisationAssoc.SportIdPrimary))
data.OrganisationAssoc.Sport = model.GetSportName(organisationAssoc.SportIdPrimary);
if (!organisation.RegionIdPrimary.Equals(organisationAssoc.RegionIdPrimary))
data.OrganisationAssoc.Region = model.GetRegionName(organisationAssoc.RegionIdPrimary);
// Association type.
bool associationReversed = itemAssociation.ItemIdTo.Id.Equals(organisation.Id);
String associationLabel = "";
if (itemAssociation.AssociationType == Sax.AssociationType.Affiliate)
associationLabel = associationReversed ? "Member organisation" : associationLabel = "Parent organisation";
else if (itemAssociation.AssociationType == Sax.AssociationType.Partner)
associationLabel = "Partner organisation";
data.Assoc.Populate(itemAssociation.AssociationType, associationReversed, associationLabel);
// Common properties.
data.StatusCode = itemAssociation.StatusCode;
data.StatusDesc = itemAssociation.StatusDesc;
}
private void PopulateAssociationResponseItemData(SaxModel_Admin model, AssociationItemData data, SaxItemAssociation itemAssociation, SaxOrganisation organisation, SaxOrganisation organisationAssoc)
{
PopulateAssociationResponseListData(model, data, itemAssociation, organisation, organisationAssoc);
}
//-------------------------------------------------------------------
// Get Organisation Association List ajax request.
//-------------------------------------------------------------------
private class AjaxResponse_AssociationList : AjaxResponse_Data
{
// Construction.
public AjaxResponse_AssociationList(SaxModel_Admin model) : base(model) { }
// Association list.
public List<AssociationListData> Associations { get; } = new List<AssociationListData>();
// Reference data.
public List<AssociationTypeData> AssociationTypes { get; } = new List<AssociationTypeData>();
public List<CodeDesc> Statuses { get; } = new List<CodeDesc>();
public String DefaultStatusCode { get; set; }
}
//-------------------------------------------------------------------
[HttpPost]
[Route("Organisation/Ajax/GetAssociationList")]
[Route("Organisation/Ajax/GetAssociations")]
public IActionResult Ajax_GetAssociationList(Model_OrganisationAssociations model)
{
String messageTitle = Localiser["caption.organisation.associations"];
AjaxResponse_AssociationList responseData = new AjaxResponse_AssociationList(model);
// Validate session.
if (!ValidUserSession)
return GetAjaxResult_InvalidSession(responseData);
// Organisation Id is mandatory.
SysId organisationId = SysId.ToSysId(model.Filter.OrganisationId);
if (!SysId.IsValid(organisationId))
{
SaLogger.Warn($"{SysUtil.Fn} Request to retrieve associations without valid organisation Id.");
return Json(responseData.SetPopupMessage(messageTitle, Localiser["msg.error.item.id.not.specified"]));
}
// Reference data - association types.
// 'Label' is option caption to add new association of the specified type.
responseData.AssociationTypes.Add(new AssociationTypeData(Sax.AssociationTypeOption.AffiliateParent));
responseData.AssociationTypes.Add(new AssociationTypeData(Sax.AssociationTypeOption.AffiliateChild));
responseData.AssociationTypes.Add(new AssociationTypeData(Sax.AssociationTypeOption.Partner));
// Reference data - statuses.
foreach (SaStatus status in SaxItemAssociation.ValidStatuses.FindAll(item => item != SaStatus.Deleted))
responseData.Statuses.Add(SacsSystem.GetStatusCode(status), Localiser.GetStatusCaption(status));
responseData.DefaultStatusCode = SacsSystem.GetStatusCode(SaxItemAssociation.DefaultStatus);
// Get organisation associations.
GetOrganisationAssociations(model, organisationId, responseData.Associations);
if (model.Paging.IsPaged)
model.Paging.ItemCount = (ushort)responseData.Associations.Count;
// Complete response.
responseData.SetPaging(model);
responseData.Success = true;
return Json(responseData);
}
private void GetOrganisationAssociations(SaxModel_Admin model, SysId organisationId, List<AssociationListData> associations)
{
// Record disposable items as they are created so all can be disposed when needed.
using (DisposableCollection<IDisposable> disposableItems = new DisposableCollection<IDisposable>())
{
// Item filter.
SaxItemAssociation.Params prmItemAssociation = new SaxItemAssociation.Params();
prmItemAssociation.ItemIdsIncluded.Add(new ItemId((short)Sax.DataItemType.Organisation, organisationId));
prmItemAssociation.StatusCat = SaxItemAssociation.StatusCat.NotDeleted;
model.Paging.ItemTotal = (ulong)ItemDataMgr.GetItemAssociationCount(prmItemAssociation);
model.PopulatePagingTo(prmItemAssociation.ResultsPaging);
// Item columns.
ColumnDfn colId = ItemDataMgr.GetItemAssociationColumnDfn(SaxItemAssociation.Column.Id);
ColumnDfn colItemTypeFrom = ItemDataMgr.GetItemAssociationColumnDfn(SaxItemAssociation.Column.ItemTypeFrom);
ColumnDfn colItemIdFrom = ItemDataMgr.GetItemAssociationColumnDfn(SaxItemAssociation.Column.ItemIdFrom);
ColumnDfn colOrganisationFrom_Name = ItemDataMgr.GetItemAssociationColumnDfn(SaxItemAssociation.Column.OrganisationFrom_Name);
ColumnDfn colOrganisationFrom_SportIdPrimary = ItemDataMgr.GetItemAssociationColumnDfn(SaxItemAssociation.Column.OrganisationFrom_SportIdPrimary);
ColumnDfn colOrganisationFrom_RegionIdPrimary = ItemDataMgr.GetItemAssociationColumnDfn(SaxItemAssociation.Column.OrganisationFrom_RegionIdPrimary);
ColumnDfn colItemTypeTo = ItemDataMgr.GetItemAssociationColumnDfn(SaxItemAssociation.Column.ItemTypeTo);
ColumnDfn colItemIdTo = ItemDataMgr.GetItemAssociationColumnDfn(SaxItemAssociation.Column.ItemIdTo);
ColumnDfn colOrganisationTo_Name = ItemDataMgr.GetItemAssociationColumnDfn(SaxItemAssociation.Column.OrganisationTo_Name);
ColumnDfn colOrganisationTo_SportIdPrimary = ItemDataMgr.GetItemAssociationColumnDfn(SaxItemAssociation.Column.OrganisationTo_SportIdPrimary);
ColumnDfn colOrganisationTo_RegionIdPrimary = ItemDataMgr.GetItemAssociationColumnDfn(SaxItemAssociation.Column.OrganisationTo_RegionIdPrimary);
ColumnDfn colAssociationType = ItemDataMgr.GetItemAssociationColumnDfn(SaxItemAssociation.Column.AssociationType);
ColumnDfn colStatus = ItemDataMgr.GetItemAssociationColumnDfn(SaxItemAssociation.Column.Status);
List<ColumnDfn> columns = new List<ColumnDfn>()
{
colId,
colItemTypeFrom, colItemIdFrom, colOrganisationFrom_Name, colOrganisationFrom_SportIdPrimary, colOrganisationFrom_RegionIdPrimary,
colItemTypeTo, colItemIdTo, colOrganisationTo_Name, colOrganisationTo_SportIdPrimary, colOrganisationTo_RegionIdPrimary,
colAssociationType,
colStatus
};
// Query items.
using (DbResultSet resultSet = ItemDataMgr.ExecuteItemAssociationQuery(columns, prmItemAssociation))
{
while (resultSet != null && resultSet.HasRows && resultSet.NextRecord())
{
NamedValue.Map values = resultSet.GetRecordValues();
AssociationListData associationData = new AssociationListData()
{
Id = values.GetString(colId.DataName),
StatusCode = values.GetString(colStatus.DataName),
StatusDesc = SacsSystem.GetStatusDescForCode(values.GetString(colStatus.DataName)),
};
// Items.
bool associationReversed = false;
ItemId itemIdFrom = new ItemId((short)values.GetLong(colItemTypeFrom.DataName), new SysId(values.GetLong(colItemIdFrom.DataName)));
String organisationNameFrom = values.GetString(colOrganisationFrom_Name.DataName);
SysId sportIdFrom = new SysId(values.GetLong(colOrganisationFrom_SportIdPrimary.DataName));
SysId regionIdFrom = new SysId(values.GetLong(colOrganisationFrom_RegionIdPrimary.DataName));
ItemId itemIdTo = new ItemId((short)values.GetLong(colItemTypeTo.DataName), new SysId(values.GetLong(colItemIdTo.DataName)));
String organisationNameTo = values.GetString(colOrganisationTo_Name.DataName);
SysId sportIdTo = new SysId(values.GetLong(colOrganisationTo_SportIdPrimary.DataName));
SysId regionIdTo = new SysId(values.GetLong(colOrganisationTo_RegionIdPrimary.DataName));
if (ItemId.IsValid(itemIdFrom) && ItemId.IsValid(itemIdTo) && itemIdFrom.Type == (int)Sax.DataItemType.Organisation && itemIdTo.Type == (int)Sax.DataItemType.Organisation)
{
if (itemIdFrom.Id.Equals(organisationId))
{
associationData.Organisation.Id = SysId.ToString(itemIdFrom.Id);
associationData.Organisation.Name = organisationNameFrom;
associationData.OrganisationAssoc.Id = SysId.ToString(itemIdTo.Id);
associationData.OrganisationAssoc.Name = organisationNameTo;
if (!sportIdFrom.Equals(sportIdTo))
associationData.OrganisationAssoc.Sport = model.GetSportName(sportIdTo);
if (!regionIdFrom.Equals(regionIdTo))
associationData.OrganisationAssoc.Sport = model.GetRegionName(regionIdTo);
}
else if (itemIdTo.Id.Equals(organisationId))
{
associationReversed = true;
associationData.Organisation.Id = SysId.ToString(itemIdTo.Id);
associationData.Organisation.Name = organisationNameTo;
associationData.OrganisationAssoc.Id = SysId.ToString(itemIdFrom.Id);
associationData.OrganisationAssoc.Name = organisationNameFrom;
if (!sportIdFrom.Equals(sportIdTo))
associationData.OrganisationAssoc.Sport = model.GetSportName(sportIdFrom);
if (!regionIdFrom.Equals(regionIdTo))
associationData.OrganisationAssoc.Sport = model.GetRegionName(regionIdFrom);
}
else if (SacsSystem.DebugMode)
// Track invalid associations, ie. where neither item is this organisation.
Debugger.Break();
}
else if (SacsSystem.DebugMode)
// Track invalid associations, ie. where either item is not an organisation.
Debugger.Break();
// Association type.
Sax.AssociationType associationType = (Sax.AssociationType)values.GetLong(colAssociationType.DataName);
associationData.Assoc.Populate(associationType, associationReversed);
// Add association to the list.
associations.Add(associationData);
}
}
}
}
//-------------------------------------------------------------------
// Get Organisation Association ajax request.
//-------------------------------------------------------------------
private class AjaxResponse_Association : AjaxResponse_Data
{
// Construction.
public AjaxResponse_Association(SaxModel_Admin model) : base(model) { }
// Association details.
public AssociationItemData Association { get; } = new AssociationItemData();
}
//-------------------------------------------------------------------
[HttpPost]
[Route("Organisation/Ajax/GetAssociation")]
public IActionResult Ajax_GetAssociation(Model_OrganisationAssociations model)
{
String messageTitle = Localiser["caption.organisation.association"];
AjaxResponse_Association responseData = new AjaxResponse_Association(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 itemAssociationId = SysId.ToSysId(model.ItemAssociationId);
if (!SysId.IsValid(itemAssociationId))
return Json(responseData.SetPopupMessage(messageTitle, Localiser["msg.error.item.id.not.specified"]));
// Retrieve association.
SaxItemAssociation itemAssociation = ItemDataMgr.GetItemAssociationById(itemAssociationId);
disposableItems.Add(itemAssociation);
if (itemAssociation == null)
{
SaLogger.Warn($"{SysUtil.Fn} Failed to retrieve Item Association for Id: '{model.ItemAssociationId}'");
return Json(responseData.SetPopupMessage(messageTitle, Localiser["msg.error.item.id.not.found"]));
}
// Identify organisations.
SysId organisationId = SysId.ToSysId(model.OrganisationId);
SysId organisationIdAssoc = null;
if (!SysId.IsValid(organisationId))
{
SaLogger.Warn($"{SysUtil.Fn} Organisation Id must be supplied.");
return Json(responseData.SetPopupMessage(messageTitle, Localiser["msg.error.data.incomplete"]));
}
// Validate the association.
if (itemAssociation.ItemIdFrom.Type != (int)Sax.DataItemType.Organisation)
{
SaLogger.Warn($"{SysUtil.Fn} Inconsistent association 'From' item type: {AssociationTypeCodes.GetCodeForType(itemAssociation.ItemIdFrom.Type)}. Expected: Organisation.");
return Json(responseData.SetPopupMessage(messageTitle, Localiser["msg.error.data.inconsistent"]));
}
if (itemAssociation.ItemIdTo.Type != (int)Sax.DataItemType.Organisation)
{
SaLogger.Warn($"{SysUtil.Fn} Inconsistent association 'To' item type: {AssociationTypeCodes.GetCodeForType(itemAssociation.ItemIdTo.Type)}. Expected: Organisation.");
return Json(responseData.SetPopupMessage(messageTitle, Localiser["msg.error.data.inconsistent"]));
}
// Identify organisations from association items.
// The specified organisation must be one or other of the association's items.
if (itemAssociation.ItemIdFrom.Id.Equals(organisationId))
organisationIdAssoc = itemAssociation.ItemIdTo.Id;
else if (itemAssociation.ItemIdTo.Id.Equals(organisationId))
organisationIdAssoc = itemAssociation.ItemIdFrom.Id;
else
{
SaLogger.Warn($"{SysUtil.Fn} Inconsistent association - Specified organisation is neither item.");
return Json(responseData.SetPopupMessage(messageTitle, Localiser["msg.error.data.inconsistent"]));
}
// Retrieve Organisation.
SaxOrganisation organisation = AccountMgr.GetOrganisationById(organisationId);
disposableItems.Add(organisation);
if (organisation == null)
{
SaLogger.Warn($"{SysUtil.Fn} Failed to retrieve Organisation for Id: '{organisationId}'");
return Json(responseData.SetPopupMessage(messageTitle, Localiser["msg.error.system"]));
}
// Retrieve associated Organisation.
SaxOrganisation organisationAssoc = AccountMgr.GetOrganisationById(organisationIdAssoc);
disposableItems.Add(organisationAssoc);
if (organisationAssoc == null)
{
SaLogger.Warn($"{SysUtil.Fn} Failed to retrieve associated Organisation for Id: '{organisationIdAssoc}'");
return Json(responseData.SetPopupMessage(messageTitle, Localiser["msg.error.system"]));
}
// Construct response.
PopulateAssociationResponseItemData(model, responseData.Association, itemAssociation, organisation, organisationAssoc);
}
responseData.Success = true;
return Json(responseData);
}
//-------------------------------------------------------------------
// Store Organisation Association ajax request.
//-------------------------------------------------------------------
[HttpPost]
[Route("Organisation/Ajax/StoreAssociation")]
public IActionResult Ajax_StoreAssociation(Model_OrganisationAssociations model)
{
String messageTitle = Localiser["caption.organisation.association"];
AjaxResponse_Association responseData = new AjaxResponse_Association(model);
// Validate session.
if (!ValidUserSession)
return GetAjaxResult_InvalidSession(responseData);
// Validate item properties.
model.ValidateOrganisationAssociationData(responseData);
if (responseData.HasControlErrors())
return Json(responseData);
// Record disposable items as they are created so all can be disposed when needed.
using (DisposableCollection<IDisposable> disposableItems = new DisposableCollection<IDisposable>())
{
// Organisation.
SysId organisationId = SysId.ToSysId(model.OrganisationId);
SaxOrganisation organisation = AccountMgr.GetOrganisationById(organisationId);
disposableItems.Add(organisation);
if (organisation == null)
{
SaLogger.Warn($"{SysUtil.Fn} Failed to retrieve Organisation for Id: {model.OrganisationId}");
return Json(responseData.SetPopupMessage(messageTitle, Localiser["msg.error.data.incomplete"]));
}
// Associated Organisation.
SysId organisationIdAssoc = SysId.ToSysId(model.OrganisationIdAssoc);
SaxOrganisation organisationAssoc = AccountMgr.GetOrganisationById(organisationIdAssoc);
disposableItems.Add(organisationAssoc);
if (organisationAssoc == null)
{
SaLogger.Warn($"{SysUtil.Fn} Failed to retrieve associated Organisation for Id: {model.OrganisationIdAssoc}");
return Json(responseData.SetPopupMessage(messageTitle, Localiser["msg.error.data.incomplete"]));
}
if (organisationIdAssoc.Equals(organisationId))
{
SaLogger.Warn($"{SysUtil.Fn} Associated organisation must not be the same as the organisation.");
return Json(responseData.SetPopupMessage(messageTitle, Localiser["msg.error.data.inconsistent"]));
}
// Association type.
Sax.AssociationType associationType = model.GetAssociationTypeForOption(model.Association.GetAssociationTypeOption());
if ((int)associationType < 1)
{
SaLogger.Warn($"{SysUtil.Fn} Association Type must be specified.");
return Json(responseData.SetPopupMessage(messageTitle, Localiser["msg.error.data.inconsistent"]));
}
Sax.AssociationTypeOption associationTypeOption = model.Association.GetAssociationTypeOption();
if ((int)associationTypeOption < 1)
{
SaLogger.Warn($"{SysUtil.Fn} Association Type Option must be specified.");
return Json(responseData.SetPopupMessage(messageTitle, Localiser["msg.error.data.inconsistent"]));
}
if (!model.IsConsistentWith(associationTypeOption, associationType))
{
SaLogger.Warn($"{SysUtil.Fn} Association Type Option ({associationTypeOption}) is inconsistent with Association Type ({associationType}).");
return Json(responseData.SetPopupMessage(messageTitle, Localiser["msg.error.data.inconsistent"]));
}
// Identify From/To items.
ItemId itemIdFrom = organisation.GetItemId();
ItemId itemIdTo = organisationAssoc.GetItemId();
if (model.IsAssociationTypeDirectionReversed(associationTypeOption))
{
itemIdFrom = organisationAssoc.GetItemId();
itemIdTo = organisation.GetItemId();
}
// Retrieve existing association.
SysId itemAssociationId = SysId.ToSysId(model.ItemAssociationId);
if (SysId.IsValid(itemAssociationId) == StringUtil.ToBool(model.NewItem))
{
// Inconsistentcy between the 'Existing' flag and whether the Item Association Id has been specified.
SaLogger.Warn($"{SysUtil.Fn} Item Association Id " + (StringUtil.ToBool(model.NewItem) ? "specified for new item" : "not specified for existing item") + ".");
return Json(responseData.SetPopupMessage(messageTitle, Localiser["msg.error.data.inconsistent"]));
}
SaxItemAssociation itemAssociation = ItemDataMgr.GetItemAssociationById(itemAssociationId);
if (itemAssociation == null && StringUtil.ToBool(model.NewItem))
{
// Check whether there's a matching association for the 'new' association with 'deleted' status.
SaxItemAssociation.Params prmItemAsociation = new SaxItemAssociation.Params();
prmItemAsociation.ItemIdsIncluded.Add(organisation.GetItemId());
prmItemAsociation.AssociationTypesIncluded.Add(associationType);
prmItemAsociation.StatusCat = SaxItemAssociation.StatusCat.DeletedOnly;
foreach (SaxItemAssociation itemAssociationCandidate in ItemDataMgr.GetItemAssociationList(prmItemAsociation))
{
if (itemAssociationCandidate.ItemIdFrom.Equals(organisationAssoc.GetItemId()) || itemAssociationCandidate.ItemIdTo.Equals(organisationAssoc.GetItemId()))
{
itemAssociation = itemAssociationCandidate;
itemAssociation.Status = SaxItemAssociation.DefaultStatus;
break;
}
disposableItems.Add(itemAssociationCandidate);
}
}
disposableItems.Add(itemAssociation);
if (itemAssociation == null && SysId.IsValid(itemAssociationId))
{
SaLogger.Warn($"{SysUtil.Fn} Failed to retrieve item association for Id: '{itemAssociationId}'");
return Json(responseData.SetPopupMessage(messageTitle, Localiser["msg.error.item.id.not.found"]));
}
if (itemAssociation != null)
{
// Check if item relationship has been reversed, eg. from parent to child.
bool associationReversed = itemAssociation.ItemIdTo.Id.Equals(organisation.Id);
if (model.IsAssociationTypeDirectionReversed(associationTypeOption) != model.IsAssociationTypeDirectionReversed(AssociationTypeData.GetAssociationTypeOption(itemAssociation.AssociationType, associationReversed)))
{
// Logical direction has been reversed from that stored.
// Items must be switched.
ItemId itemAssociationItemIdFrom = itemAssociation.ItemIdFrom;
itemAssociation.ItemIdFrom = itemAssociation.ItemIdTo;
itemAssociation.ItemIdTo = itemAssociationItemIdFrom;
}
// Validate From/To items.
if (!itemAssociation.ItemIdFrom.Equals(itemIdFrom))
{
SaLogger.Warn($"{SysUtil.Fn} Inconsistent association - expected 'From' Item Id: {SysId.ToString(itemIdFrom.Id)}. Found: {SysId.ToString(itemAssociation.ItemIdFrom.Id)}");
return Json(responseData.SetPopupMessage(messageTitle, Localiser["msg.error.system"]));
}
if (!itemAssociation.ItemIdTo.Equals(itemIdTo))
{
SaLogger.Warn($"{SysUtil.Fn} Inconsistent association - expected 'To' Item Id: {SysId.ToString(itemIdTo.Id)}. Found: {SysId.ToString(itemAssociation.ItemIdTo.Id)}");
return Json(responseData.SetPopupMessage(messageTitle, Localiser["msg.error.system"]));
}
}
else
{
// Item Association not identified.
// Check for deleted association that can be reused.
SaxItemAssociation.Params itemAssociationParams = new SaxItemAssociation.Params();
if (itemAssociation != null)
itemAssociation.Status = SaxItemAssociation.DefaultStatus;
else
{
// Association not found - create a new one.
itemAssociation = ItemDataMgr.CreateItemAssociation(itemIdFrom, itemIdTo, associationType);
}
disposableItems.Add(itemAssociation);
}
// Populate item properties.
if (model.Association.StatusCode != null)
itemAssociation.Status = model.Association.GetStatus();
// Store item.
if (!SysId.IsValid(itemAssociation.Id))
{
if (!ItemDataMgr.InsertItemAssociation(itemAssociation, DbUpdateInfo))
{
SaLogger.Warn($"{SysUtil.Fn} Failed to insert item association: {ItemDataMgr.ErrorStateMessage}");
return Json(responseData.SetPopupMessage(messageTitle, Localiser["msg.error.item.insert.failed"] + "\n\n" + ItemDataMgr.ErrorStateMessage));
}
}
else
{
if (!ItemDataMgr.UpdateItemAssociation(itemAssociation, DbUpdateInfo))
{
SaLogger.Warn($"{SysUtil.Fn} Failed to update item association [Id:{SysId.ToString(itemAssociation.Id)}]: {ItemDataMgr.ErrorStateMessage}");
return Json(responseData.SetPopupMessage(messageTitle, Localiser["msg.error.item.update.failed"] + "\n\n" + ItemDataMgr.ErrorStateMessage));
}
}
// Construct response.
PopulateAssociationResponseItemData(model, responseData.Association, itemAssociation, organisation, organisationAssoc);
}
responseData.Success = true;
return Json(responseData);
}
//-------------------------------------------------------------------
// Remove Organisation Association ajax request.
//-------------------------------------------------------------------
[HttpPost]
[Route("Organisation/Ajax/RemoveAssociation")]
public IActionResult Ajax_RemoveAssociation(Model_OrganisationAssociations model)
{
String messageTitle = Localiser["caption.organisation.association"];
AjaxResponse_Association responseData = new AjaxResponse_Association(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>())
{
// Organisation.
SysId organisationId = SysId.ToSysId(model.OrganisationId);
SaxOrganisation organisation = AccountMgr.GetOrganisationById(organisationId);
disposableItems.Add(organisation);
if (organisation == null)
{
SaLogger.Warn($"{SysUtil.Fn} Failed to retrieve Organisation for Id: '{model.OrganisationId}'");
return Json(responseData.SetPopupMessage(messageTitle, Localiser["msg.error.data.incomplete"]));
}
// Association Organisation.
SysId organisationIdAssoc = SysId.ToSysId(model.OrganisationIdAssoc);
SaxOrganisation organisationAssoc = AccountMgr.GetOrganisationById(organisationIdAssoc);
disposableItems.Add(organisationAssoc);
if (organisationAssoc == null)
{
SaLogger.Warn($"{SysUtil.Fn} Failed to retrieve associated Organisation for Id: '{model.OrganisationIdAssoc}'");
return Json(responseData.SetPopupMessage(messageTitle, Localiser["msg.error.data.incomplete"]));
}
if (organisationIdAssoc.Equals(organisationId))
{
SaLogger.Warn($"{SysUtil.Fn} Associated organisation must not be the same as the organisation.");
return Json(responseData.SetPopupMessage(messageTitle, Localiser["msg.error.data.inconsistent"]));
}
// Item Association.
SysId itemAssociationId = SysId.ToSysId(model.ItemAssociationId);
SaxItemAssociation itemAssociation = ItemDataMgr.GetItemAssociationById(itemAssociationId);
disposableItems.Add(itemAssociation);
if (itemAssociation == null)
{
SaLogger.Warn($"{SysUtil.Fn} Failed to retrieve Item Association for Id: '{itemAssociationId}'");
return Json(responseData.SetPopupMessage(messageTitle, Localiser["msg.error.item.id.not.found"]));
}
// Validate association Item Ids.
if (!itemAssociation.ItemIdFrom.Id.Equals(organisation.Id) && !itemAssociation.ItemIdTo.Id.Equals(organisation.Id))
{
SaLogger.Warn($"{SysUtil.Fn} Inconsistent item association properties. Organisation must be 'From' or 'To' item");
return Json(responseData.SetPopupMessage(messageTitle, Localiser["msg.error.data.inconsistent"]));
}
if (!itemAssociation.ItemIdFrom.Id.Equals(organisationAssoc.Id) && !itemAssociation.ItemIdTo.Id.Equals(organisationAssoc.Id))
{
SaLogger.Warn($"{SysUtil.Fn} Inconsistent item association properties. Associated Organisation must be 'From' or 'To' item");
return Json(responseData.SetPopupMessage(messageTitle, Localiser["msg.error.data.inconsistent"]));
}
// Logically delete item.
itemAssociation.Status = SaStatus.Deleted;
if (!ItemDataMgr.UpdateItemAssociation(itemAssociation, DbUpdateInfo))
{
SaLogger.Warn($"{SysUtil.Fn} Failed to update Item Association [Id:{SysId.ToString(itemAssociation.Id)}]: {ItemDataMgr.ErrorStateMessage}");
return Json(responseData.SetPopupMessage(messageTitle, Localiser["msg.error.item.update.failed"] + "\n\n" + ItemDataMgr.ErrorStateMessage));
}
// Construct response.
PopulateAssociationResponseItemData(model, responseData.Association, itemAssociation, organisation, organisationAssoc);
}
responseData.Success = true;
return Json(responseData);
}
//-------------------------------------------------------------------
}
//=======================================================================
}