diff --git a/SaxoniteCoreWebAdmin/Admin/Controllers/OrganisationController-Ajax-Associations.cs b/SaxoniteCoreWebAdmin/Admin/Controllers/OrganisationController-Ajax-Associations.cs index 7a4977f..0278b99 100644 --- a/SaxoniteCoreWebAdmin/Admin/Controllers/OrganisationController-Ajax-Associations.cs +++ b/SaxoniteCoreWebAdmin/Admin/Controllers/OrganisationController-Ajax-Associations.cs @@ -12,7 +12,6 @@ using System.Collections.Generic; using System.Diagnostics; using System.Linq; using System.Threading.Tasks; -using AssociationTypeOption = Saxonite.Net.Core.Web.Admin.Models.Model_OrganisationAssociations.AssociationTypeOption; namespace Saxonite.Net.Core.Web.Admin.Controllers { @@ -25,7 +24,7 @@ namespace Saxonite.Net.Core.Web.Admin.Controllers // Association type combinations. //------------------------------------------------------------------- private static readonly String optionLabel_AffiliatedTo = "Affiliated to"; - private static readonly String optionLabel_Affiliate = "Affiliate"; + 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 @@ -36,12 +35,12 @@ namespace Saxonite.Net.Core.Web.Admin.Controllers { // No parameter construction - populating association details for individual item (see Populate()). } - public AssociationTypeData(AssociationTypeOption associationTypeOption) + public AssociationTypeData(Sax.AssociationTypeOption associationTypeOption) { // Population on construction - generic association type definitions, eg. for drop down menu options. Populate(associationTypeOption); } - public void Populate(AssociationTypeOption associationTypeOption) + public void Populate(Sax.AssociationTypeOption associationTypeOption) { // Default labels - option to select association etc. Type = ((int)associationTypeOption).ToString(); @@ -55,7 +54,7 @@ namespace Saxonite.Net.Core.Web.Admin.Controllers public void Populate(Sax.AssociationType associationType, bool reversed, String label) { // Populate for specific labels, eg. to identify nature of associated organisation. - AssociationTypeOption associationTypeOption = GetAssociationTypeOption(associationType, reversed); + Sax.AssociationTypeOption associationTypeOption = GetAssociationTypeOption(associationType, reversed); Type = ((int)associationTypeOption).ToString(); Label = label; } @@ -63,22 +62,22 @@ namespace Saxonite.Net.Core.Web.Admin.Controllers public String Type { get; set; } public String Label { get; set; } // Utilities. - public static AssociationTypeOption GetAssociationTypeOption(Sax.AssociationType associationType, bool reversed) + public static Sax.AssociationTypeOption GetAssociationTypeOption(Sax.AssociationType associationType, bool reversed) { switch (associationType) { - case Sax.AssociationType.Affiliate: return reversed ? AssociationTypeOption.AffiliatedTo : AssociationTypeOption.AffiliateParent; - case Sax.AssociationType.Partner: return AssociationTypeOption.Partner; + case Sax.AssociationType.Affiliate: return reversed ? Sax.AssociationTypeOption.AffiliateChild : Sax.AssociationTypeOption.AffiliateParent; + case Sax.AssociationType.Partner: return Sax.AssociationTypeOption.Partner; } - return AssociationTypeOption.Unknown; + return Sax.AssociationTypeOption.Unknown; } - public static String GetOptionLabel(AssociationTypeOption associationTypeOption) + public static String GetOptionLabel(Sax.AssociationTypeOption associationTypeOption) { switch (associationTypeOption) { - case AssociationTypeOption.AffiliateParent: return optionLabel_AffiliatedTo; - case AssociationTypeOption.AffiliatedTo: return optionLabel_Affiliate; - case AssociationTypeOption.Partner: return optionLabel_Partner; + case Sax.AssociationTypeOption.AffiliateParent: return optionLabel_AffiliatedTo; + case Sax.AssociationTypeOption.AffiliateChild: return optionLabel_Affiliate; + case Sax.AssociationTypeOption.Partner: return optionLabel_Partner; } return ""; } @@ -93,6 +92,8 @@ namespace Saxonite.Net.Core.Web.Admin.Controllers { 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(); @@ -101,7 +102,7 @@ namespace Saxonite.Net.Core.Web.Admin.Controllers private class AssociationItemData : AssociationListData { } - private void PopulateAssociationResponseListData(AssociationListData data, SaxItemAssociation itemAssociation, SaxOrganisation organisation, SaxOrganisation organisationAssoc) + private void PopulateAssociationResponseListData(SaxModel_Admin model, AssociationListData data, SaxItemAssociation itemAssociation, SaxOrganisation organisation, SaxOrganisation organisationAssoc) { data.Id = SysId.ToString(itemAssociation.Id); // Organisation. @@ -110,6 +111,10 @@ namespace Saxonite.Net.Core.Web.Admin.Controllers // 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 = ""; @@ -122,9 +127,9 @@ namespace Saxonite.Net.Core.Web.Admin.Controllers data.StatusCode = itemAssociation.StatusCode; data.StatusDesc = itemAssociation.StatusDesc; } - private void PopulateAssociationResponseItemData(AssociationItemData data, SaxItemAssociation itemAssociation, SaxOrganisation organisation, SaxOrganisation organisationAssoc) + private void PopulateAssociationResponseItemData(SaxModel_Admin model, AssociationItemData data, SaxItemAssociation itemAssociation, SaxOrganisation organisation, SaxOrganisation organisationAssoc) { - PopulateAssociationResponseListData(data, itemAssociation, organisation, organisationAssoc); + PopulateAssociationResponseListData(model, data, itemAssociation, organisation, organisationAssoc); } //------------------------------------------------------------------- @@ -165,9 +170,9 @@ namespace Saxonite.Net.Core.Web.Admin.Controllers // Reference data - association types. // 'Label' is option caption to add new association of the specified type. - responseData.AssociationTypes.Add(new AssociationTypeData(AssociationTypeOption.AffiliateParent)); - responseData.AssociationTypes.Add(new AssociationTypeData(AssociationTypeOption.AffiliatedTo)); - responseData.AssociationTypes.Add(new AssociationTypeData(AssociationTypeOption.Partner)); + 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)) @@ -204,21 +209,26 @@ namespace Saxonite.Net.Core.Web.Admin.Controllers 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 columns = new List() - { - colId, - colItemTypeFrom, colItemIdFrom, - colItemTypeTo, colItemIdTo, - colAssociationType, - colStatus - }; + { + colId, + colItemTypeFrom, colItemIdFrom, colOrganisationFrom_Name, colOrganisationFrom_SportIdPrimary, colOrganisationFrom_RegionIdPrimary, + colItemTypeTo, colItemIdTo, colOrganisationTo_Name, colOrganisationTo_SportIdPrimary, colOrganisationTo_RegionIdPrimary, + colAssociationType, + colStatus + }; // Query items. - List organisationIds = new List(); using (DbResultSet resultSet = ItemDataMgr.ExecuteItemAssociationQuery(columns, prmItemAssociation)) { while (resultSet != null && resultSet.HasRows && resultSet.NextRecord()) @@ -231,23 +241,39 @@ namespace Saxonite.Net.Core.Web.Admin.Controllers StatusDesc = SacsSystem.GetStatusDescForCode(values.GetString(colStatus.DataName)), }; // Items. - bool assiationReversed = false; + 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) { - organisationIds.AddId(itemIdFrom.Id); - organisationIds.AddId(itemIdTo.Id); 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)) { - assiationReversed = true; + 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. @@ -258,22 +284,11 @@ namespace Saxonite.Net.Core.Web.Admin.Controllers Debugger.Break(); // Association type. Sax.AssociationType associationType = (Sax.AssociationType)values.GetLong(colAssociationType.DataName); - associationData.Assoc.Populate(associationType, assiationReversed); + associationData.Assoc.Populate(associationType, associationReversed); // Add association to the list. associations.Add(associationData); } } - // Populate organisation details. - SaxOrganisation.Params prmOrganisation = new SaxOrganisation.Params(); - prmOrganisation.IdsIncluded.AddRange(organisationIds); - prmOrganisation.StatusCat = SaxOrganisation.StatusCat.Any; - List organisations = AccountMgr.GetOrganisationList(prmOrganisation); - disposableItems.AddRange(organisations); - foreach (AssociationListData associationData in associations) - { - associationData.Organisation.Name = organisations.GetOrganisationName(new SysId(StringUtil.ToLong(associationData.Organisation.Id))); - associationData.OrganisationAssoc.Name = organisations.GetOrganisationName(new SysId(StringUtil.ToLong(associationData.OrganisationAssoc.Id))); - } } } @@ -365,12 +380,12 @@ namespace Saxonite.Net.Core.Web.Admin.Controllers disposableItems.Add(organisationAssoc); if (organisationAssoc == null) { - SaLogger.Warn(fn + " Failed to retrieve Organisation for Id: '" + organisationIdAssoc + "'"); + SaLogger.Warn(fn + " Failed to retrieve associated Organisation for Id: '" + organisationIdAssoc + "'"); return Json(responseData.SetMessage(messageTitle, Localiser["msg.error.system"])); } // Construct response. - PopulateAssociationResponseItemData(responseData.Association, itemAssociation, organisation, organisationAssoc); + PopulateAssociationResponseItemData(model, responseData.Association, itemAssociation, organisation, organisationAssoc); } responseData.Success = true; @@ -433,7 +448,7 @@ namespace Saxonite.Net.Core.Web.Admin.Controllers SaLogger.Warn(fn + " Association Type must be specified."); return Json(responseData.SetMessage(messageTitle, Localiser["msg.error.data.inconsistent"])); } - AssociationTypeOption associationTypeOption = model.Association.GetAssociationTypeOption(); + Sax.AssociationTypeOption associationTypeOption = model.Association.GetAssociationTypeOption(); if ((int)associationTypeOption < 1) { SaLogger.Warn(fn + " Association Type Option must be specified."); @@ -463,6 +478,24 @@ namespace Saxonite.Net.Core.Web.Admin.Controllers return Json(responseData.SetMessage(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)) { @@ -472,15 +505,25 @@ namespace Saxonite.Net.Core.Web.Admin.Controllers 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(fn + " Inconsistent association - expected 'From' Item Id: " + SysId.ToString(organisation.Id) + ". Found: " + SysId.ToString(itemAssociation.ItemIdFrom.Id)); + SaLogger.Warn(fn + " Inconsistent association - expected 'From' Item Id: " + SysId.ToString(itemIdFrom.Id) + ". Found: " + SysId.ToString(itemAssociation.ItemIdFrom.Id)); return Json(responseData.SetMessage(messageTitle, Localiser["msg.error.system"])); } if (!itemAssociation.ItemIdTo.Equals(itemIdTo)) { - SaLogger.Warn(fn + " Inconsistent association - expected 'To' Item Id: " + SysId.ToString(organisation.Id) + ". Found: " + SysId.ToString(itemAssociation.ItemIdTo.Id)); + SaLogger.Warn(fn + " Inconsistent association - expected 'To' Item Id: " + SysId.ToString(itemIdTo.Id) + ". Found: " + SysId.ToString(itemAssociation.ItemIdTo.Id)); return Json(responseData.SetMessage(messageTitle, Localiser["msg.error.system"])); } } @@ -510,7 +553,7 @@ namespace Saxonite.Net.Core.Web.Admin.Controllers { if (!ItemDataMgr.InsertItemAssociation(itemAssociation, DbUpdateInfo)) { - SaLogger.Warn(fn + " Failed to insert user organisation: " + ItemDataMgr.ErrorStateMessage); + SaLogger.Warn(fn + " Failed to insert item association: " + ItemDataMgr.ErrorStateMessage); return Json(responseData.SetMessage(messageTitle, Localiser["msg.error.item.insert.failed"] + "\n\n" + ItemDataMgr.ErrorStateMessage)); } } @@ -518,13 +561,13 @@ namespace Saxonite.Net.Core.Web.Admin.Controllers { if (!ItemDataMgr.UpdateItemAssociation(itemAssociation, DbUpdateInfo)) { - SaLogger.Warn(fn + " Failed to update user organisation [Id:" + SysId.ToString(itemAssociation.Id) + "]: " + ItemDataMgr.ErrorStateMessage); + SaLogger.Warn(fn + " Failed to update item association [Id:" + SysId.ToString(itemAssociation.Id) + "]: " + ItemDataMgr.ErrorStateMessage); return Json(responseData.SetMessage(messageTitle, Localiser["msg.error.item.update.failed"] + "\n\n" + ItemDataMgr.ErrorStateMessage)); } } // Construct response. - PopulateAssociationResponseItemData(responseData.Association, itemAssociation, organisation, organisationAssoc); + PopulateAssociationResponseItemData(model, responseData.Association, itemAssociation, organisation, organisationAssoc); } responseData.Success = true; @@ -588,12 +631,12 @@ namespace Saxonite.Net.Core.Web.Admin.Controllers // Validate association Item Ids. if (!itemAssociation.ItemIdFrom.Id.Equals(organisation.Id) && !itemAssociation.ItemIdTo.Id.Equals(organisation.Id)) { - SaLogger.Warn(fn + " Inconsistent user organisation properties. Organisation must be 'From' or 'To' item"); + SaLogger.Warn(fn + " Inconsistent item association properties. Organisation must be 'From' or 'To' item"); return Json(responseData.SetMessage(messageTitle, Localiser["msg.error.data.inconsistent"])); } if (!itemAssociation.ItemIdFrom.Id.Equals(organisationAssoc.Id) && !itemAssociation.ItemIdTo.Id.Equals(organisationAssoc.Id)) { - SaLogger.Warn(fn + " Inconsistent user organisation properties. Associated Organisation must be 'From' or 'To' item"); + SaLogger.Warn(fn + " Inconsistent item association properties. Associated Organisation must be 'From' or 'To' item"); return Json(responseData.SetMessage(messageTitle, Localiser["msg.error.data.inconsistent"])); } @@ -606,7 +649,7 @@ namespace Saxonite.Net.Core.Web.Admin.Controllers } // Construct response. - PopulateAssociationResponseItemData(responseData.Association, itemAssociation, organisation, organisationAssoc); + PopulateAssociationResponseItemData(model, responseData.Association, itemAssociation, organisation, organisationAssoc); } responseData.Success = true; diff --git a/SaxoniteCoreWebAdmin/Admin/Controllers/OrganisationController-Ajax.cs b/SaxoniteCoreWebAdmin/Admin/Controllers/OrganisationController-Ajax.cs index 126d012..4e50231 100644 --- a/SaxoniteCoreWebAdmin/Admin/Controllers/OrganisationController-Ajax.cs +++ b/SaxoniteCoreWebAdmin/Admin/Controllers/OrganisationController-Ajax.cs @@ -142,7 +142,7 @@ namespace Saxonite.Net.Core.Web.Admin.Controllers // Reference data - contact methods. List validContactMethods = SaxOrganisation.ValidContactMethods; - foreach (TypeCodeDesc contactMethod in ContactMethodCodes) + foreach (TypeCodeDesc contactMethod in ContactMethodCodes.OrderBy(item => item.Desc)) { // Skip methods that aren't associated with Organisations. if (!validContactMethods.Contains((Sax.ContactMethod)contactMethod.Type)) continue; diff --git a/SaxoniteCoreWebAdmin/Admin/Models/Model_OrganisationAssociations.cs b/SaxoniteCoreWebAdmin/Admin/Models/Model_OrganisationAssociations.cs index 9a05f9f..131330a 100644 --- a/SaxoniteCoreWebAdmin/Admin/Models/Model_OrganisationAssociations.cs +++ b/SaxoniteCoreWebAdmin/Admin/Models/Model_OrganisationAssociations.cs @@ -21,13 +21,6 @@ namespace Saxonite.Net.Core.Web.Admin.Models { //------------------------------------------------------------------- // Definitions. - public enum AssociationTypeOption - { - Unknown = 0, - AffiliateParent, - AffiliatedTo, - Partner, - } //------------------------------------------------------------------- // Construction. @@ -59,7 +52,7 @@ namespace Saxonite.Net.Core.Web.Admin.Models { // Association type option. public String TypeOption { get; set; } - public AssociationTypeOption GetAssociationTypeOption() { return EnumUtil.ParseEnum(TypeOption, AssociationTypeOption.Unknown); } + public Sax.AssociationTypeOption GetAssociationTypeOption() { return EnumUtil.ParseEnum(TypeOption, Sax.AssociationTypeOption.Unknown); } // Association properties. public String StatusCode { get; set; } public SaStatus GetStatus() { return SacsSystem.GetStatusForCode(StatusCode); } @@ -68,35 +61,35 @@ namespace Saxonite.Net.Core.Web.Admin.Models //------------------------------------------------------------------- // Utilities. - public bool IsAssociationTypeDirectionReversed(AssociationTypeOption associationTypeOption) + public bool IsAssociationTypeDirectionReversed(Sax.AssociationTypeOption associationTypeOption) { switch (associationTypeOption) { - case AssociationTypeOption.AffiliatedTo: + case Sax.AssociationTypeOption.AffiliateChild: return true; } return false; } - public Sax.AssociationType GetAssociationTypeForOption(AssociationTypeOption associationTypeOption) + public Sax.AssociationType GetAssociationTypeForOption(Sax.AssociationTypeOption associationTypeOption) { switch (associationTypeOption) { - case AssociationTypeOption.AffiliateParent: - case AssociationTypeOption.AffiliatedTo: + case Sax.AssociationTypeOption.AffiliateParent: + case Sax.AssociationTypeOption.AffiliateChild: return Sax.AssociationType.Affiliate; - case AssociationTypeOption.Partner: + case Sax.AssociationTypeOption.Partner: return Sax.AssociationType.Partner; } return Sax.AssociationType.Unknown; } - public bool IsConsistentWith(AssociationTypeOption associationTypeOption, Sax.AssociationType associationType) + public bool IsConsistentWith(Sax.AssociationTypeOption associationTypeOption, Sax.AssociationType associationType) { switch (associationType) { case Sax.AssociationType.Affiliate: - return new List() { AssociationTypeOption.AffiliatedTo, AssociationTypeOption.AffiliateParent }.Contains(associationTypeOption); + return new List() { Sax.AssociationTypeOption.AffiliateChild, Sax.AssociationTypeOption.AffiliateParent }.Contains(associationTypeOption); case Sax.AssociationType.Partner: - return new List() { AssociationTypeOption.Partner }.Contains(associationTypeOption); + return new List() { Sax.AssociationTypeOption.Partner }.Contains(associationTypeOption); } return false; } diff --git a/SaxoniteCoreWebAdmin/ReadMe.md b/SaxoniteCoreWebAdmin/ReadMe.md new file mode 100644 index 0000000..a495f79 --- /dev/null +++ b/SaxoniteCoreWebAdmin/ReadMe.md @@ -0,0 +1,19 @@ +# Saxonite Admin application + +System management functions to maintain data. + +## Technology + +- VS2022, .Net Core 3.1 + +## Lifecycle + +### Build + +- 'Package' project to local folder: + - Delete all files prior to publish. + +### Deployment + +- Replace application files under IIS folder. +- Verify database schema on startup. \ No newline at end of file diff --git a/SaxoniteCoreWebAdmin/Views/Common/Controls-ItemContactMethods.cshtml b/SaxoniteCoreWebAdmin/Views/Common/Controls-ItemContactMethods.cshtml index 5542d30..1d7d84f 100644 --- a/SaxoniteCoreWebAdmin/Views/Common/Controls-ItemContactMethods.cshtml +++ b/SaxoniteCoreWebAdmin/Views/Common/Controls-ItemContactMethods.cshtml @@ -22,8 +22,16 @@ .ItemContactMethods .ContactMethods table.sax-data .sax-label { - margin:0; - padding:0 8px 0 0; + margin: 0; + padding: 0 8px 0 0; + white-space: nowrap; + } + + .ItemContactMethods .ContactMethods table.sax-data .sax-value + { + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; } @@ -53,8 +61,8 @@ - - + +
diff --git a/SaxoniteCoreWebAdmin/Views/Organisation/Associations.cshtml b/SaxoniteCoreWebAdmin/Views/Organisation/Associations.cshtml index ec06288..b901e56 100644 --- a/SaxoniteCoreWebAdmin/Views/Organisation/Associations.cshtml +++ b/SaxoniteCoreWebAdmin/Views/Organisation/Associations.cshtml @@ -69,14 +69,14 @@
- @* element templates *@ + @* Item templates *@
@* Organisation Association item template *@
@* User name *@ -
+
@* Burger menu *@ @@ -90,8 +90,9 @@
-
-
+
+
+
diff --git a/SaxoniteCoreWebAdmin/wwwroot/js/sax-org-ass.js b/SaxoniteCoreWebAdmin/wwwroot/js/sax-org-ass.js index 9143d53..f66b80e 100644 --- a/SaxoniteCoreWebAdmin/wwwroot/js/sax-org-ass.js +++ b/SaxoniteCoreWebAdmin/wwwroot/js/sax-org-ass.js @@ -5,9 +5,8 @@ (function (win, doc, undef) { "use strict"; - let oa = sax.mdl.organisationAssociations = { data: {}, ajax: {} }; + let oa = sax.mdl.organisationAssociations = { data: {}, ajax: {}, itemIdPrefix: 'oa_' }; let $pnl = oa.$pnl = {}, $frm = oa.$frm = {}, $dlg = oa.$dlg = {}; - let mdlOrg = function () { return sax.mdl.organisations; } //-------------------------------------------------------------- oa.refreshResults = function (delay) @@ -48,19 +47,19 @@ if (!items.length) return $msgs.append($('
' + sax.msg.dataNoneFound + '
')).fadeIn(500); for (let itemIdx = 0; itemIdx < items.length; itemIdx++) - $items.append(sax.populateItem(oa.getItemTemplate(), items[itemIdx])); + $items.append(oa.populateItem(oa.getItemTemplate(), items[itemIdx])); $items.fadeIn(50); } - sax.populateItem = function ($item, item) + oa.populateItem = function ($item, item) { if (!item) return; - $item.prop('id', oa.itemIdPrefix + item.id); - $('.UserEmailAddress', $item).text(item.emailAddress); - $('.UserStatusDesc', $item).text(item.userStatusDesc).addClass('sax-status-' + (item.userStatusCode || '').toLowerCase()); - $('.UserStatus', $item).toggle(!item.userActive); - $('.UserDisplayName', $item).text(item.displayName); - $('.AssociationRoleCount .RoleCount', $item).text(item.roleCount); - $('.StatusDesc', $item).text(item.statusDesc).addClass('sax-status-' + (item.statusCode || '').toLowerCase()); + $item.prop('id', oa.getItemIdProp(item.id)); + let assoc = item.assoc || {}; + let org = item.organisationAssoc || {}; + $('.OrganisationName', $item).text(org.name); + $('.AssociationLabel', $item).text(assoc.label); + $('.Sport', $item).text(org.sport); + $('.Region', $item).text(org.region); $('.sax-caption .sax-action').toggleClass('sax-action', !item.deleted); let $mnu = $('ul.sax-dropdown-menu', $item).empty(); if (!item.deleted) @@ -68,8 +67,7 @@ let fnUpdItm = function () { oa.updateItem(item, $item) }; $mnu.append(oa.getItemMenuOption(sax.lbl.edit, fnUpdItm, 'sax-edit')); $item.on('click', '.sax-header .sax-caption', fnUpdItm).on('dblclick', fnUpdItm); - if (item.association) - $mnu.append(oa.getItemMenuOption(sax.lbl.remove, function () { oa.removeItem(item, $item) }, 'sax-remove')); + $mnu.append(oa.getItemMenuOption(sax.lbl.remove, function () { oa.removeItem(item, $item) }, 'sax-remove')); } $('.sax-burger', $item).toggle(!!$('.sax-dropdown-item', $mnu).length); $('.sax-panel-messages', $pnl.results).hide().empty(); @@ -77,6 +75,10 @@ } //-------------------------------------------------------------- + oa.getItemIdProp = function (id) + { + return oa.itemIdPrefix + id; + } oa.getFilterDropdownTemplate = function () { return $('.sax-panel-item-template .sax-dropdown-menu.AssociationFilter li.sax-dropdown-item', $pnl.filter).clone(); @@ -150,8 +152,9 @@ //-------------------------------------------------------------- oa.updateItem = function (item, $item) { - $('.OrganisationId', $frm.orgId).val(item.id); - oa.ajax.getItemAssoc = oa.ajax.getItemAssoc || { url: sax.url.base + 'Organisation/Ajax/GetAssociation', formId: 'frmOrganisationId', callbackFn: oa.onUpdateItem }; + $('.ItemAssociationId', $frm.assocId).val(item.id); + $('.OrganisationId', $frm.assocId).val($('.OrganisationId', $frm.filter).val()); + oa.ajax.getItemAssoc = oa.ajax.getItemAssoc || { url: sax.url.base + 'Organisation/Ajax/GetAssociation', formId: 'frmAssociationId', callbackFn: oa.onUpdateItem }; sax.doAjaxRequest(oa.ajax.getItemAssoc, 50); } oa.onUpdateItem = function (response) @@ -160,13 +163,19 @@ if (!response.success) return; oa.clearItem(); // Association. - let item = response.organisation; - let assoc = item.association || {}; + let item = response.association || {}; + let assoc = item.assoc || {}; + let org = item.organisation || {}; + let orgAssoc = item.organisationAssoc || {}; let frm = $frm.assoc; $('input.ItemAssociationId', frm).val(item.id); - $('input.OrganisationId', frm).val(item.organisationId); - $('.OrganisationName', frm).val(item.name); + $('input.NewItem', frm).val(0); + $('input.OrganisationId', frm).val(org.id); + $('.OrganisationName input.OrganisationName', frm).val(org.name); + $('.AssociationTypeOption select.AssociationTypeOption', frm).val(assoc.type); + $('.OrganisationIdAssoc input.OrganisationIdAssoc', frm).val(orgAssoc.id); + $('.OrganisationIdAssoc .OrganisationNameAssoc', frm).val(orgAssoc.name); sax.openAssocDialog(); } @@ -183,7 +192,36 @@ sax.refreshControlValues(response.controlValues, $frm.assoc); if (!response.success) return; $dlg.assoc.dialog('close'); - mdlOrg().refreshItem(response.organisation); + oa.refreshItem(response.association); + } + oa.refreshItem = function (item) + { + var $item = $('#' + oa.getItemIdProp(item.id)); + if (!$item.length) + { + $item = oa.getItemTemplate(); + $('.sax-panel-item-list', $pnl.results).prepend($item).show(); + } + oa.populateItem($item, item); + } + + //-------------------------------------------------------------- + oa.removeItem = function (item, $item) + { + sax.debug('removeItem') + oa.clearItem(); + let frm = $frm.assoc; + $('.ItemAssociationId', frm).val(item.id); + $('.OrganisationId', frm).val(item.organisation.id); + $('.OrganisationIdAssoc', frm).val(item.organisationAssoc.id); + oa.ajax.getRemoveItem = oa.ajax.getRemoveItem || { url: sax.url.base + 'Organisation/Ajax/RemoveAssociation', formId: frm.prop('id'), callbackFn: oa.onRemoveItem }; + sax.doAjaxRequest(oa.ajax.getRemoveItem, 50); + } + oa.onRemoveItem = function (response) + { + sax.debug('', response) + if (!response.success) return; + oa.refreshResults(); } //-------------------------------------------------------------- diff --git a/SaxoniteCoreWebAdmin/wwwroot/js/sax-org.js b/SaxoniteCoreWebAdmin/wwwroot/js/sax-org.js index b7d46b7..bb69dcf 100644 --- a/SaxoniteCoreWebAdmin/wwwroot/js/sax-org.js +++ b/SaxoniteCoreWebAdmin/wwwroot/js/sax-org.js @@ -247,7 +247,7 @@ op.clearItem = function () { let frm = $frm.org; - $('.OrganisationId,.OrganisationSportId,.OrganisationRegionIdPrimary,input[type="text"],textarea,select', frm).val(''); + $('.OrganisationId,.OrganisationSportId,.OrganisationRegionIdPrimary,input[name="ContactMethods"],input[type="text"],textarea,select', frm).val(''); $pnl.tblconMths.empty(); $('.OrganisationStatus', frm).val(op.data.defaultStatusCode); sax.clearControlErrors(); diff --git a/SaxoniteCoreWebOnline/Localisation/SaxOnlineProperties.resx b/SaxoniteCoreWebOnline/Localisation/SaxOnlineProperties.resx index 2150198..27b8b18 100644 --- a/SaxoniteCoreWebOnline/Localisation/SaxOnlineProperties.resx +++ b/SaxoniteCoreWebOnline/Localisation/SaxOnlineProperties.resx @@ -297,6 +297,9 @@ Article + + Associations + At diff --git a/SaxoniteCoreWebOnline/Online/Controllers/SearchController-Ajax-Organisations.cs b/SaxoniteCoreWebOnline/Online/Controllers/SearchController-Ajax-Organisations.cs index 2ae2383..374ef8d 100644 --- a/SaxoniteCoreWebOnline/Online/Controllers/SearchController-Ajax-Organisations.cs +++ b/SaxoniteCoreWebOnline/Online/Controllers/SearchController-Ajax-Organisations.cs @@ -16,6 +16,8 @@ using Sarand.Net.Standard.Base.Core; using Sarand.Net.Standard.Base.Dbas; using Saxonite.Net.Standard.Base.App; using Saxonite.Net.Core.Web.Online.Helpers; +using Org.BouncyCastle.Security; +using static Saxonite.Net.Standard.Base.App.Sax; namespace Saxonite.Net.Core.Web.Online.Controllers { @@ -135,6 +137,35 @@ namespace Saxonite.Net.Core.Web.Online.Controllers //------------------------------------------------------------------- // Account - Get Organisation. //------------------------------------------------------------------- + /// + /// Associations are grouped by type and direction (parent->child). + ///
The organisation specified in each case is the 'other' organisation (not the current 'target' organisation).
+ ///
+ private class AssociationTypeData + { + public String Caption { get; set; } + public List Organisations { get; } = new List(); + } + /// + /// Association Organisation - the one that is not the current 'target' organisation. + ///
Region and Sport are only populate if different from the 'target' organisation.
+ ///
+ private class AssociationOrganisationData + { + public String Name { get; set; } + public String Display + { + get + { + String display = Name; + if (!StringUtil.IsBlank(Sport) || !StringUtil.IsBlank(Region)) + display += " (" + String.Join(", ", new List { Sport, Region }.Where(item => !StringUtil.IsBlank(item))) + ")"; + return display; + } + } + public String Region { get; set; } + public String Sport { get; set; } + } private class OrganisationItemData : OrganisationListData { public String TypeDesc { get; set; } @@ -143,6 +174,7 @@ namespace Saxonite.Net.Core.Web.Online.Controllers public List Contacts { get; set; } public String Address { get; set; } public String PostalCode { get; set; } + public List Associations { get; } = new List(); } private class AjaxResponse_Search_Organisation : AjaxResponse_Data { @@ -228,6 +260,83 @@ namespace Saxonite.Net.Core.Web.Online.Controllers organisationData.Address = (new List() { addressLines, addressTownProvince }).Concat("\n", "", "", true, true); organisationData.PostalCode = address.PostalCode; } + + // Associations. + Dictionary> associationTypeGroups = new Dictionary>(); + ColumnDfn colId = ItemDataMgr.GetItemAssociationColumnDfn(SaxItemAssociation.Column.Id); + ColumnDfn colAssociationType = ItemDataMgr.GetItemAssociationColumnDfn(SaxItemAssociation.Column.AssociationType); + 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); + List associationCols = new List() + { + colId, colAssociationType, + colItemTypeFrom, colItemIdFrom, colOrganisationFrom_Name, colOrganisationFrom_SportIdPrimary, colOrganisationFrom_RegionIdPrimary, + colItemTypeTo, colItemIdTo, colOrganisationTo_Name, colOrganisationTo_SportIdPrimary, colOrganisationTo_RegionIdPrimary + }; + SaxItemAssociation.Params prmAssociations = new SaxItemAssociation.Params(); + prmAssociations.ItemIdsIncluded.Add(organisation.GetItemId()); + prmAssociations.StatusCat = SaxItemAssociation.StatusCat.ActiveOnly; + using (DbResultSet resultSet = ItemDataMgr.ExecuteItemAssociationQuery(associationCols, prmAssociations)) + { + while (resultSet != null && resultSet.HasRows && resultSet.NextRecord()) + { + NamedValue.Map values = resultSet.GetRecordValues(); + SysId associationId = new SysId(values.GetLong(colId.DataName)); + Sax.AssociationType associationType = (Sax.AssociationType)values.GetLong(colAssociationType.DataName); + ItemId itemIdFrom = new ItemId((short)values.GetLong(colItemTypeFrom.DataName), new SysId(values.GetLong(colItemIdFrom.DataName))); + ItemId itemIdTo = new ItemId((short)values.GetLong(colItemTypeTo.DataName), new SysId(values.GetLong(colItemIdTo.DataName))); + bool associationTypeDirectionReversed = associationType == Sax.AssociationType.Affiliate && itemIdTo.Id.Equals(organisation.Id); + // Determine association type group. + SaxItemAssociation.TypeOptionData assocTypeOption = new SaxItemAssociation.TypeOptionData(associationType, associationTypeDirectionReversed, GetAssociationTypeOptionCaption(associationType, associationTypeDirectionReversed)); + List organisations = associationTypeGroups.GetValue(assocTypeOption, null); + if (organisations == null) + associationTypeGroups.Add(assocTypeOption, organisations = new List()); + // Populate organisation properties. + SysId orgSportIdFrom = new SysId(values.GetLong(colOrganisationFrom_SportIdPrimary.DataName)); + SysId orgRegionIdFrom = new SysId(values.GetLong(colOrganisationFrom_RegionIdPrimary.DataName)); + SysId orgSportIdTo = new SysId(values.GetLong(colOrganisationTo_SportIdPrimary.DataName)); + SysId orgRegionIdTo = new SysId(values.GetLong(colOrganisationTo_RegionIdPrimary.DataName)); + if (itemIdTo.Id.Equals(organisation.Id)) + { + // 'From' item is the 'Other' organisation. + AssociationOrganisationData assocOrgData = new AssociationOrganisationData(); + assocOrgData.Name = values.GetString(colOrganisationFrom_Name.DataName); + if (!orgSportIdFrom.Equals(orgSportIdTo)) + assocOrgData.Sport = model.GetSportCaption(orgSportIdFrom); + if (!orgRegionIdFrom.Equals(orgRegionIdTo)) + assocOrgData.Region = model.GetRegionCaption(orgRegionIdFrom); + organisations.Add(assocOrgData); + } + else + { + // 'To' item is the 'Other' organisation. + AssociationOrganisationData assocOrgData = new AssociationOrganisationData(); + assocOrgData.Name = values.GetString(colOrganisationTo_Name.DataName); + if (!orgSportIdFrom.Equals(orgSportIdTo)) + assocOrgData.Sport = model.GetSportCaption(orgSportIdTo); + if (!orgRegionIdFrom.Equals(orgRegionIdTo)) + assocOrgData.Region = model.GetRegionCaption(orgRegionIdTo); + organisations.Add(assocOrgData); + } + } + } + // Populate associations to response. + foreach (SaxItemAssociation.TypeOptionData associationTypeGroup in associationTypeGroups.Keys.OrderBy(item => (int)item.TypeOption)) + { + AssociationTypeData associationTypeData = new AssociationTypeData(); + associationTypeData.Caption = associationTypeGroup.Caption; + associationTypeData.Organisations.AddRange(associationTypeGroups.GetValue(associationTypeGroup, new List()).OrderBy(item => item.Name).ToList()); + responseData.Organisation.Associations.Add(associationTypeData); + } + } // Complete response. @@ -235,6 +344,21 @@ namespace Saxonite.Net.Core.Web.Online.Controllers return Json(responseData); } + //------------------------------------------------------------------- + /// + /// Caption to display above list of corresponding associated organisation for the 'target' organisation. + /// + private String GetAssociationTypeOptionCaption(Sax.AssociationType associationType, bool reversed) + { + switch (SaxItemAssociation.GetAssociationTypeOption(associationType, reversed)) + { + case AssociationTypeOption.AffiliateParent: return "Affiliated to"; + case AssociationTypeOption.AffiliateChild: return "Affiliates"; + case AssociationTypeOption.Partner: return "Partners"; + } + return "Unknown relationship"; + } + //------------------------------------------------------------------- } //======================================================================= diff --git a/SaxoniteCoreWebOnline/Views/Search/Panel-OrganisationDialog.cshtml b/SaxoniteCoreWebOnline/Views/Search/Panel-OrganisationDialog.cshtml index 9e014bc..f309297 100644 --- a/SaxoniteCoreWebOnline/Views/Search/Panel-OrganisationDialog.cshtml +++ b/SaxoniteCoreWebOnline/Views/Search/Panel-OrganisationDialog.cshtml @@ -9,16 +9,28 @@ { margin-right: 8px; } - #dlgOrganisation .OrganisationAbout .sax-value, #dlgOrganisation .OrganisationFacilities .sax-value + + #dlgOrganisation .OrganisationAssociations .sax-value, #dlgOrganisation .OrganisationAbout .sax-value, #dlgOrganisation .OrganisationFacilities .sax-value { min-height: 40px; max-height: 170px; - padding:2px 4px; + padding: 2px 4px; border: 1px solid #a0a0a0; border-top-left-radius: 4px; border-bottom-left-radius: 4px; overflow-y: scroll; } + + #dlgOrganisation .OrganisationAssociations .sax-value .sax-assoc-type + { + font-weight: bold; + text-decoration: underline; + } + + #dlgOrganisation .OrganisationAssociations .sax-value .sax-assoc-org + { + padding-left: 12px; + } @@ -63,6 +75,10 @@
@Localiser["label.postal.code"]
+
+
@Localiser["label.associations"]
+
+
diff --git a/SaxoniteCoreWebOnline/wwwroot/js/sax-src-org.js b/SaxoniteCoreWebOnline/wwwroot/js/sax-src-org.js index f1b5192..8b18ba7 100644 --- a/SaxoniteCoreWebOnline/wwwroot/js/sax-src-org.js +++ b/SaxoniteCoreWebOnline/wwwroot/js/sax-src-org.js @@ -139,6 +139,14 @@ $('.OrganisationLocation', dlg).toggle(!!item.regionName).find('.sax-value').text(item.regionName); $('.OrganisationAbout', dlg).toggle(!!item.about).find('.sax-value').html((item.about || '').replace('\n', '
')); $('.OrganisationFacilities', dlg).toggle(!!item.facilities).find('.sax-value').html((item.facilities || '').replace('\n', '
')); + let $assocs = $('.OrganisationAssociations .sax-value', dlg).empty(); + for (const assoc of item.associations) + { + $assocs.append($("
").text(assoc.caption)); + for (const org of assoc.organisations) + $assocs.append($("
").text(org.display)); + } + $('.OrganisationAssociations', dlg).toggle(item.associations.length>0); let contact = []; for (let cnt of item.contacts || []) contact.push(cnt.value); diff --git a/SaxoniteStandardBase/Base/App/Sax-Version.cs b/SaxoniteStandardBase/Base/App/Sax-Version.cs index 8cb2447..c705833 100644 --- a/SaxoniteStandardBase/Base/App/Sax-Version.cs +++ b/SaxoniteStandardBase/Base/App/Sax-Version.cs @@ -18,7 +18,7 @@ namespace Saxonite.Net.Standard.Base.App //------------------------------------------------------------------- public static class Version { - private static String version { get; } = "1.2.10.6"; + private static String version { get; } = "1.2.11.0"; // Retrieve version. public static String GetFullVersion () { return version; } public static String GetAppVersion () @@ -40,9 +40,12 @@ namespace Saxonite.Net.Standard.Base.App //------------------------------------------------------------------- // Version history. //------------------------------------------------------------------- + // 1.2.11 6-Jul-2023 + // - Organisation associations. // 1.2.10 11-Oct-2022 // - Online organisations display issue fixed. // - User Sport 'Preparing' options. + // - User reports. // 1.2.9 12-Oct-2022 // - Online Sports Experience update bug resolved. // - Sports Experience Awards. diff --git a/SaxoniteStandardBase/Base/App/Sax.cs b/SaxoniteStandardBase/Base/App/Sax.cs index 88a6946..ed5bc17 100644 --- a/SaxoniteStandardBase/Base/App/Sax.cs +++ b/SaxoniteStandardBase/Base/App/Sax.cs @@ -310,6 +310,13 @@ namespace Saxonite.Net.Standard.Base.App } //------------------------------------------------------------------- + // Associations. + + /// + /// Association type. + ///
- Affiliate - parent-child relationship.
+ ///
- Partner - equal sided relationship.
+ ///
public enum AssociationType { Unknown = 0, @@ -320,6 +327,20 @@ namespace Saxonite.Net.Standard.Base.App Partner = 20, } + /// + /// Association type option. + ///
- Encompasses association type and 'direction' from the perspective of one of the organisations.
+ ///
- Affiliate relationship is either from the parent or to the child.
+ ///
- Partner relationships are directionless.
+ ///
+ public enum AssociationTypeOption + { + Unknown = 0, + AffiliateParent, + AffiliateChild, + Partner, + } + //------------------------------------------------------------------- public enum AddressType { @@ -358,6 +379,8 @@ namespace Saxonite.Net.Standard.Base.App Twitter = 310, Facebook = 320, Instagram = 330, + YouTube = 340, + Twitch = 350, } //------------------------------------------------------------------- @@ -407,7 +430,7 @@ namespace Saxonite.Net.Standard.Base.App { Unknown = 0, /* Do not change the values or codes - tied to database. */ - Enhancement= 10, + Enhancement = 10, Information = 20, Bug = 30, } diff --git a/SaxoniteStandardBase/Base/App/SaxSystem-CodeTypes.cs b/SaxoniteStandardBase/Base/App/SaxSystem-CodeTypes.cs index cca9141..a7e5647 100644 --- a/SaxoniteStandardBase/Base/App/SaxSystem-CodeTypes.cs +++ b/SaxoniteStandardBase/Base/App/SaxSystem-CodeTypes.cs @@ -272,6 +272,8 @@ namespace Saxonite.Net.Standard.Base.App contactMethodCodes.Add(Sax.ContactMethod.Twitter, Sax.ContactMethod.Twitter.ToString(), "Twitter"); contactMethodCodes.Add(Sax.ContactMethod.Facebook, Sax.ContactMethod.Facebook.ToString(), "Facebook"); contactMethodCodes.Add(Sax.ContactMethod.Instagram, Sax.ContactMethod.Instagram.ToString(), "Instagram"); + contactMethodCodes.Add(Sax.ContactMethod.YouTube, Sax.ContactMethod.YouTube.ToString(), "YouTube"); + contactMethodCodes.Add(Sax.ContactMethod.Twitch, Sax.ContactMethod.Twitch.ToString(), "Twitch"); //--------------------------------------------------------------- // Event types. diff --git a/SaxoniteStandardBase/Base/Bus/SaxItemAssociation.cs b/SaxoniteStandardBase/Base/Bus/SaxItemAssociation.cs index f530d6a..32dccee 100644 --- a/SaxoniteStandardBase/Base/Bus/SaxItemAssociation.cs +++ b/SaxoniteStandardBase/Base/Bus/SaxItemAssociation.cs @@ -1,9 +1,15 @@ -using Sarand.Net.Standard.Base.Bus; +using Org.BouncyCastle.Utilities; +using Sarand.Net.Standard.Base.Bus; using Sarand.Net.Standard.Base.Core; +using Sarand.Net.Standard.Base.Utils; using Saxonite.Net.Standard.Base.App; using System; using System.Collections.Generic; +using System.Data; using System.Text; +using static Sarand.Net.Standard.Base.Bus.ItemId; +using static Sarand.Net.Standard.Base.ItemOptions.SaItemOptionDfn; +using static Saxonite.Net.Standard.Base.App.Sax; namespace Saxonite.Net.Standard.Base.Bus { @@ -30,6 +36,13 @@ namespace Saxonite.Net.Standard.Base.Bus ItemTypeTo_Desc, ItemIdTo, ItemTo_Caption, + // Specific item properties. + OrganisationFrom_Name, + OrganisationFrom_SportIdPrimary, + OrganisationFrom_RegionIdPrimary, + OrganisationTo_Name, + OrganisationTo_SportIdPrimary, + OrganisationTo_RegionIdPrimary, // Association type. AssociationType, AssociationType_Code, @@ -45,6 +58,40 @@ namespace Saxonite.Net.Standard.Base.Bus UpdateOn_Date, } + //------------------------------------------------------------------- + public class TypeOptionData + { + public TypeOptionData(Sax.AssociationTypeOption typeOption, String caption) + { + TypeOption = typeOption; + Caption = StringUtil.OnBlank(caption); + } + public TypeOptionData(Sax.AssociationType associationType, bool reversed, String caption) : + this(GetAssociationTypeOption(associationType, reversed), caption) + { + } + // Properties. + public AssociationTypeOption TypeOption { get; } + public String Caption { get; } + // Comparison. + public override bool Equals(object obj) + { + if (obj == null) return false; + return Equals(obj as TypeOptionData); + } + public bool Equals(TypeOptionData value) + { + if (value == null) return false; + if (this.TypeOption != value.TypeOption) return false; + if (!this.Caption.EqualsCI(value.Caption)) return false; + return true; + } + public override int GetHashCode() + { + return HashCode.Combine(TypeOption, Caption); + } + } + //------------------------------------------------------------------- /// Properties other than common. public class DataProperties @@ -172,6 +219,19 @@ namespace Saxonite.Net.Standard.Base.Bus } return itemTypes; } + /// Determin type option from type and parent/child direction. + /// + /// + /// + public static AssociationTypeOption GetAssociationTypeOption(Sax.AssociationType associationType, bool reversed) + { + switch (associationType) + { + case Sax.AssociationType.Affiliate: return reversed ? AssociationTypeOption.AffiliateChild : AssociationTypeOption.AffiliateParent; + case Sax.AssociationType.Partner: return AssociationTypeOption.Partner; + } + return AssociationTypeOption.Unknown; + } //------------------------------------------------------------------- // Properties. @@ -191,17 +251,17 @@ namespace Saxonite.Net.Standard.Base.Bus public ItemId ItemIdFrom { get { return data.ItemIdFrom; } - protected set { data.ItemIdFrom = value; } + set { data.ItemIdFrom = value; } } public ItemId ItemIdTo { get { return data.ItemIdTo; } - protected set { data.ItemIdTo = value; } + set { data.ItemIdTo = value; } } public Sax.AssociationType AssociationType { get { return data.AssociationType; } - protected set { data.AssociationType = value; } + set { data.AssociationType = value; } } //------------------------------------------------------------------- diff --git a/SaxoniteStandardBase/Base/Bus/SaxOrganisation.cs b/SaxoniteStandardBase/Base/Bus/SaxOrganisation.cs index c59d86e..452065b 100644 --- a/SaxoniteStandardBase/Base/Bus/SaxOrganisation.cs +++ b/SaxoniteStandardBase/Base/Bus/SaxOrganisation.cs @@ -210,6 +210,8 @@ namespace Saxonite.Net.Standard.Base.Bus Sax.ContactMethod.Twitter, Sax.ContactMethod.Facebook, Sax.ContactMethod.Instagram, + Sax.ContactMethod.YouTube, + Sax.ContactMethod.Twitch, }; public static List ValidContactMethods { get { return new List(validContactMethods); } } diff --git a/SaxoniteStandardBase/Base/Helpers/SaxContactMethodHelper.cs b/SaxoniteStandardBase/Base/Helpers/SaxContactMethodHelper.cs index 31290d2..7ad8f3c 100644 --- a/SaxoniteStandardBase/Base/Helpers/SaxContactMethodHelper.cs +++ b/SaxoniteStandardBase/Base/Helpers/SaxContactMethodHelper.cs @@ -78,6 +78,16 @@ namespace Saxonite.Net.Standard.Base.Helpers // Instagram. return SacsDataValidationHelper.IsValidInstagramUrl(value); } + else if (method == Sax.ContactMethod.YouTube) + { + // YouTube. + return SacsDataValidationHelper.IsValidYouTubeUrl(value); + } + else if (method == Sax.ContactMethod.Twitch) + { + // Twitch. + return SacsDataValidationHelper.IsValidTwitchUrl(value); + } else { // Generic URL. diff --git a/SaxoniteStandardDbas/Dbas/Mgr/SaxItemDataMgr_Dbas-ItemAssociation.cs b/SaxoniteStandardDbas/Dbas/Mgr/SaxItemDataMgr_Dbas-ItemAssociation.cs index 800844d..392a895 100644 --- a/SaxoniteStandardDbas/Dbas/Mgr/SaxItemDataMgr_Dbas-ItemAssociation.cs +++ b/SaxoniteStandardDbas/Dbas/Mgr/SaxItemDataMgr_Dbas-ItemAssociation.cs @@ -251,6 +251,10 @@ namespace Saxonite.Net.Standard.Dbas.Mgr //------------------------------------------------------------------- // Item column definitions. private static readonly String aliasItemAssociation = "ItmAss"; + private static readonly String aliasItemAssociation_OrganisationFrom = "ItmAss_OrgFr"; + private static readonly String prefixItemAssociation_OrganisationFrom = aliasItemAssociation_OrganisationFrom + "_"; + private static readonly String aliasItemAssociation_OrganisationTo = "ItmAss_OrgTo"; + private static readonly String prefixItemAssociation_OrganisationTo = aliasItemAssociation_OrganisationTo + "_"; private void SetItemAssociationColDfn (ColumnDfn col, String caption, int width, SaxDbDfn_Core.ColumnIdx dbCol, ColumnAlignment align = ColumnAlignment.Default, DataValue.DataType dataType = DataValue.DataType.String, @@ -317,6 +321,33 @@ namespace Saxonite.Net.Standard.Dbas.Mgr columnDfn.ReadOnly = true; break; + //----------------------------------------------------------- + // Specific item properties. + case SaxItemAssociation.Column.OrganisationFrom_Name: + SetItemAssociationColDfn(columnDfn, "From organisation", 120, SaxDbDfn_Core.ColumnIdx.Organisation_Name, ColumnAlignment.Left, DataValue.DataType.String, aliasItemAssociation_OrganisationFrom, prefixItemAssociation_OrganisationFrom); + columnDfn.ReadOnly = true; + break; + case SaxItemAssociation.Column.OrganisationFrom_SportIdPrimary: + SetItemAssociationColDfn(columnDfn, "From org sport #", 80, SaxDbDfn_Core.ColumnIdx.Organisation_SportIdPrimary, ColumnAlignment.Right, DataValue.DataType.Long, aliasItemAssociation_OrganisationFrom, prefixItemAssociation_OrganisationFrom); + columnDfn.ReadOnly = true; + break; + case SaxItemAssociation.Column.OrganisationFrom_RegionIdPrimary: + SetItemAssociationColDfn(columnDfn, "From org region #", 80, SaxDbDfn_Core.ColumnIdx.Organisation_RegionIdPrimary, ColumnAlignment.Right, DataValue.DataType.Long, aliasItemAssociation_OrganisationFrom, prefixItemAssociation_OrganisationFrom); + columnDfn.ReadOnly = true; + break; + case SaxItemAssociation.Column.OrganisationTo_Name: + SetItemAssociationColDfn(columnDfn, "To organisation", 120, SaxDbDfn_Core.ColumnIdx.Organisation_Name, ColumnAlignment.Left, DataValue.DataType.String, aliasItemAssociation_OrganisationTo, prefixItemAssociation_OrganisationTo); + columnDfn.ReadOnly = true; + break; + case SaxItemAssociation.Column.OrganisationTo_SportIdPrimary: + SetItemAssociationColDfn(columnDfn, "To org sport #", 80, SaxDbDfn_Core.ColumnIdx.Organisation_SportIdPrimary, ColumnAlignment.Right, DataValue.DataType.Long, aliasItemAssociation_OrganisationTo, prefixItemAssociation_OrganisationTo); + columnDfn.ReadOnly = true; + break; + case SaxItemAssociation.Column.OrganisationTo_RegionIdPrimary: + SetItemAssociationColDfn(columnDfn, "To org region #", 80, SaxDbDfn_Core.ColumnIdx.Organisation_RegionIdPrimary, ColumnAlignment.Right, DataValue.DataType.Long, aliasItemAssociation_OrganisationTo, prefixItemAssociation_OrganisationTo); + columnDfn.ReadOnly = true; + break; + //----------------------------------------------------------- // Association type. case SaxItemAssociation.Column.AssociationType: @@ -582,6 +613,39 @@ namespace Saxonite.Net.Standard.Dbas.Mgr if (columns.HasId(SaxItemAssociation.Column.UpdateOn_Date)) query.Columns.Add(SaxDbDfn_Core.GetColumnNameQual(SaxDbDfn_Core.ColumnIdx.ItemAssociation_UpdateOn, aliasItemAssociation)); + //--------------------------------------------------------------------- + // + if (columns.HasId(SaxItemAssociation.Column.OrganisationFrom_Name)) + { + // Organisation join. + DbQueryJoin joinOrganisation = new DbQueryJoin(DbJoinType.LeftOuter); + joinOrganisation.Table = new StringPair(SaxDbDfn_Core.GetTableName(SaxDbDfn_Core.TableIdx.Organisation), aliasItemAssociation_OrganisationFrom); + joinOrganisation.Filters.Add(SaxDbDfn_Core.GetColumnNameQual(SaxDbDfn_Core.ColumnIdx.ItemAssociation_ItemTypeFrom, aliasItemAssociation) + "=" + (int)Sax.DataItemType.Organisation); + joinOrganisation.Filters.Add(SaxDbDfn_Core.GetColumnNameQual(SaxDbDfn_Core.ColumnIdx.Organisation_Id, aliasItemAssociation_OrganisationFrom) + "=" + SaxDbDfn_Core.GetColumnNameQual(SaxDbDfn_Core.ColumnIdx.ItemAssociation_ItemIdFrom, aliasItemAssociation)); + query.Joins.Add(joinOrganisation); + List OrganisationCols = new List(); + OrganisationCols.Add(SaxDbDfn_Core.ColumnIdx.Organisation_Name); + OrganisationCols.Add(SaxDbDfn_Core.ColumnIdx.Organisation_RegionIdPrimary); + OrganisationCols.Add(SaxDbDfn_Core.ColumnIdx.Organisation_SportIdPrimary); + foreach (var col in OrganisationCols) + query.Columns.Add(SaxDbDfn_Core.GetColumnNameQual(col, aliasItemAssociation_OrganisationFrom), prefixItemAssociation_OrganisationFrom + SaxDbDfn_Core.GetColumnName(col, false)); + } + if (columns.HasId(SaxItemAssociation.Column.OrganisationTo_Name)) + { + // Organisation join. + DbQueryJoin joinOrganisation = new DbQueryJoin(DbJoinType.LeftOuter); + joinOrganisation.Table = new StringPair(SaxDbDfn_Core.GetTableName(SaxDbDfn_Core.TableIdx.Organisation), aliasItemAssociation_OrganisationTo); + joinOrganisation.Filters.Add(SaxDbDfn_Core.GetColumnNameQual(SaxDbDfn_Core.ColumnIdx.ItemAssociation_ItemTypeTo, aliasItemAssociation) + "=" + (int)Sax.DataItemType.Organisation); + joinOrganisation.Filters.Add(SaxDbDfn_Core.GetColumnNameQual(SaxDbDfn_Core.ColumnIdx.Organisation_Id, aliasItemAssociation_OrganisationTo) + "=" + SaxDbDfn_Core.GetColumnNameQual(SaxDbDfn_Core.ColumnIdx.ItemAssociation_ItemIdTo, aliasItemAssociation)); + query.Joins.Add(joinOrganisation); + List OrganisationCols = new List(); + OrganisationCols.Add(SaxDbDfn_Core.ColumnIdx.Organisation_Name); + OrganisationCols.Add(SaxDbDfn_Core.ColumnIdx.Organisation_RegionIdPrimary); + OrganisationCols.Add(SaxDbDfn_Core.ColumnIdx.Organisation_SportIdPrimary); + foreach (var col in OrganisationCols) + query.Columns.Add(SaxDbDfn_Core.GetColumnNameQual(col, aliasItemAssociation_OrganisationTo), prefixItemAssociation_OrganisationTo + SaxDbDfn_Core.GetColumnName(col, false)); + } + //--------------------------------------------------------------------- // Populate filter. query.Filters.AddRange(GetItemAssociationQueryFilter(prm));